From e95be86ccde6c4997fd87729cc655c9072f865fd Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 11:45:41 -0600 Subject: Replace old storage modules. --- cryptodev.py | 256 ---- fsset.py | 3149 ---------------------------------------- iscsi.py | 332 ----- lvm.py | 614 -------- partRequests.py | 1057 -------------- partitions.py | 1899 ------------------------ raid.py | 228 --- storage/__init__.py | 1369 +++++++++++++++++ storage/deviceaction.py | 300 ++++ storage/devicelibs/__init__.py | 0 storage/devicelibs/crypto.py | 195 +++ storage/devicelibs/dm.py | 77 + storage/devicelibs/lvm.py | 289 ++++ storage/devicelibs/mdraid.py | 226 +++ storage/devicelibs/swap.py | 103 ++ storage/devices.py | 2338 +++++++++++++++++++++++++++++ storage/devicetree.py | 999 +++++++++++++ storage/errors.py | 99 ++ storage/formats/__init__.py | 308 ++++ storage/formats/dmraid.py | 89 ++ storage/formats/fs.py | 856 +++++++++++ storage/formats/luks.py | 223 +++ storage/formats/lvmpv.py | 117 ++ storage/formats/mdraid.py | 96 ++ storage/formats/swap.py | 137 ++ storage/iscsi.py | 332 +++++ storage/miscutils.py | 58 + storage/partitioning.py | 735 ++++++++++ storage/storage_log.py | 13 + storage/udev.py | 273 ++++ storage/zfcp.py | 262 ++++ zfcp.py | 262 ---- 32 files changed, 9494 insertions(+), 7797 deletions(-) delete mode 100644 cryptodev.py delete mode 100644 fsset.py delete mode 100644 iscsi.py delete mode 100644 lvm.py delete mode 100644 partRequests.py delete mode 100644 partitions.py delete mode 100644 raid.py create mode 100644 storage/__init__.py create mode 100644 storage/deviceaction.py create mode 100644 storage/devicelibs/__init__.py create mode 100644 storage/devicelibs/crypto.py create mode 100644 storage/devicelibs/dm.py create mode 100644 storage/devicelibs/lvm.py create mode 100644 storage/devicelibs/mdraid.py create mode 100644 storage/devicelibs/swap.py create mode 100644 storage/devices.py create mode 100644 storage/devicetree.py create mode 100644 storage/errors.py create mode 100644 storage/formats/__init__.py create mode 100644 storage/formats/dmraid.py create mode 100644 storage/formats/fs.py create mode 100644 storage/formats/luks.py create mode 100644 storage/formats/lvmpv.py create mode 100644 storage/formats/mdraid.py create mode 100644 storage/formats/swap.py create mode 100644 storage/iscsi.py create mode 100644 storage/miscutils.py create mode 100644 storage/partitioning.py create mode 100644 storage/storage_log.py create mode 100644 storage/udev.py create mode 100644 storage/zfcp.py delete mode 100644 zfcp.py diff --git a/cryptodev.py b/cryptodev.py deleted file mode 100644 index 63dcd4a63..000000000 --- a/cryptodev.py +++ /dev/null @@ -1,256 +0,0 @@ -# -# cryptodev.py -# -# Copyright (C) 2007 Red Hat, Inc. All rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Author(s): Dave Lehman -# - -import os -import iutil - -import logging -log = logging.getLogger("anaconda") - -def isLuks(device): - if not device.startswith("/"): - device = "/dev/" + device - rc = iutil.execWithRedirect("cryptsetup", - ["isLuks", device], - stdout = "/dev/null", - stderr = "/dev/null", - searchPath = 1) - if rc: - return False - else: - return True - -def luksUUID(device): - if not device.startswith("/"): - device = "/dev/" + device - - if not isLuks(device): - return None - - uuid = iutil.execWithCapture("cryptsetup", ["luksUUID", device]) - uuid = uuid.strip() - return uuid - -class LUKSDevice: - """LUKSDevice represents an encrypted block device using LUKS/dm-crypt. - It requires an underlying block device and a passphrase to become - functional.""" - def __init__(self, device=None, passphrase=None, format=0): - self._device = None - self.__passphrase = "" - self.name = "" - self.uuid = None - self.nameLocked = False - self.format = format - self.preexist = not format - self.packages = ["cryptsetup-luks"] - self.scheme = "LUKS" - - self.setDevice(device) - self.setPassphrase(passphrase) - if self.getUUID(): - name = "%s-%s" % (self.scheme.lower(), self.uuid) - self.setName(name, lock=True) - - def getScheme(self): - """Returns the name of the encryption scheme used by the device.""" - return self.scheme - - def setDevice(self, device): - if self._device == device: - return - - self._device = device - if device is not None: - # this will be temporary, but may be useful for debugging - name = "%s-%s" % (self.scheme.lower(), - os.path.basename(device)) - self.setName(name) - - def getDevice(self, encrypted=0): - if encrypted: - dev = self._device - else: - dev = "mapper/%s" % (self.name,) - - return dev - - def getUUID(self): - if self.format: - # self.format means we're going to reformat but haven't yet - # so we shouldn't act like there's anything worth seeing there - return - - if not self.uuid: - self.uuid = luksUUID(self.getDevice(encrypted=1)) - - return self.uuid - - def setName(self, name, lock=False): - """Set the name of the mapped device, eg: 'dmcrypt-sda3'""" - if self.name == name: - return - - if self.name and not self.getStatus(): - raise RuntimeError, "Cannot rename an active mapping." - - if self.nameLocked: - log.debug("Failed to change locked mapping name: %s" % - (self.name,)) - return - - self.name = name - if lock and name: - # don't allow anyone to lock the name as "" or None - self.nameLocked = True - - def setPassphrase(self, passphrase): - """Set the (plaintext) passphrase used to access the device.""" - self.__passphrase = passphrase - - def hasPassphrase(self): - return self.__passphrase not in (None, "") - - def crypttab(self): - """Return a crypttab formatted line describing this mapping.""" - format = "%-23s %-15s %s\n" - line = format % (self.name, - "UUID=%s" % (self.getUUID(),), - "none") - return line - - def getStatus(self): - """0 means active, 1 means inactive (or non-existent)""" - if not self.name: - return 1 - - rc = iutil.execWithRedirect("cryptsetup", - ["status", self.name], - stdout = "/dev/null", - stderr = "/dev/null", - searchPath = 1) - return rc - - def formatDevice(self): - """Write a LUKS header onto the device.""" - if not self.format: - return - - if not self.getStatus(): - log.debug("refusing to format active mapping %s" % (self.name,)) - return 1 - - if not self.hasPassphrase(): - raise RuntimeError, "Cannot create mapping without a passphrase." - - device = self.getDevice(encrypted=1) - if not device: - raise ValueError, "Cannot open mapping without a device." - - log.info("formatting %s as %s" % (device, self.getScheme())) - p = os.pipe() - os.write(p[1], "%s\n" % (self.__passphrase,)) - os.close(p[1]) - - rc = iutil.execWithRedirect("cryptsetup", - ["-q", "luksFormat", - "/dev/%s" % (device,)], - stdin = p[0], - stdout = "/dev/null", - stderr = "/dev/tty5", - searchPath = 1) - self.format = 0 - return rc - - def openDevice(self): - if not self.getStatus(): - # already mapped - return 0 - - if not self.hasPassphrase(): - raise RuntimeError, "Cannot create mapping without a passphrase." - - device = self.getDevice(encrypted=1) - if not device: - raise ValueError, "Cannot open mapping without a device." - - uuid = self.getUUID() - if not uuid: - raise RuntimeError, "Device has no UUID." - - self.setName("%s-%s" % (self.scheme.lower(), uuid), lock=True) - - log.info("mapping %s device %s to %s" % (self.getScheme(), - device, - self.name)) - - p = os.pipe() - os.write(p[1], "%s\n" % (self.__passphrase,)) - os.close(p[1]) - - rc = iutil.execWithRedirect("cryptsetup", - ["luksOpen", - "/dev/%s" % (device,), - self.name], - stdin = p[0], - stdout = "/dev/null", - stderr = "/dev/tty5", - searchPath = 1) - return rc - - def closeDevice(self): - if self.getStatus(): - # not mapped - return 0 - - log.info("unmapping %s device %s" % (self.getScheme(), self.name)) - rc = iutil.execWithRedirect("cryptsetup", - ["luksClose", self.name], - stdout = "/dev/null", - stderr = "/dev/tty5", - searchPath = 1) - return rc - - def addPassphrase(self, newpass): - if not newpass: - return 1 - - if newpass == self.__passphrase: - return 0 - - p = os.pipe() - os.write(p[1], "%s\n%s" % (self.__passphrase, newpass)) - os.close(p[1]) - - device = self.getDevice(encrypted=1) - log.info("adding new passphrase to %s device %s" % (self.getScheme(), - device)) - rc = iutil.execWithRedirect("cryptsetup", - ["-q", - "luksAddKey", - "/dev/%s" % (device,)], - stdin = p[0], - stdout = "/dev/null", - stderr = "/dev/tty5", - searchPath = 1) - - return rc - diff --git a/fsset.py b/fsset.py deleted file mode 100644 index fc9af3d8f..000000000 --- a/fsset.py +++ /dev/null @@ -1,3149 +0,0 @@ -# -# fsset.py: filesystem management -# -# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. -# All rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Author(s): Matt Wilson -# Jeremy Katz -# - -import math -import string -import isys -import iutil -import os -import resource -import posix -import stat -import errno -import parted -import sys -import struct -import partitions -import partedUtils -import raid -import lvm -import time -import types -from flags import flags -from constants import * - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("anaconda") - -class SuspendError(Exception): - pass - -class OldSwapError(Exception): - pass - -class ResizeError(Exception): - pass - -defaultMountPoints = ['/', '/boot', '/home', '/tmp', '/usr', '/var', '/usr/local', '/opt'] - -if iutil.isS390(): - # Many s390 have 2G DASDs, we recomment putting /usr/share on its own DASD - defaultMountPoints.insert(5, '/usr/share') - -if iutil.isEfi(): - defaultMountPoints.insert(2, '/boot/efi') - -fileSystemTypes = {} - -def fileSystemTypeGetDefault(): - if fileSystemTypeGet('ext4').isSupported(): - return fileSystemTypeGet('ext4') - if fileSystemTypeGet('ext3').isSupported(): - return fileSystemTypeGet('ext3') - elif fileSystemTypeGet('ext2').isSupported(): - return fileSystemTypeGet('ext2') - else: - raise ValueError, "None of ext4, ext3 or ext2 is supported in your kernel!" - -def fileSystemTypeGetDefaultBoot(): - if fileSystemTypeGet('ext3').isSupported(): - return fileSystemTypeGet('ext3') - elif fileSystemTypeGet('ext2').isSupported(): - return fileSystemTypeGet('ext2') - else: - raise ValueError, "You have neither ext3 or ext2 support in your kernel!" - -def fileSystemTypeGet(key): - if fileSystemTypes.has_key(key): - return fileSystemTypes[key] - else: - return fileSystemTypeGetDefault() - -def fileSystemTypeRegister(klass): - fileSystemTypes[klass.getName()] = klass - -def fileSystemTypeGetTypes(): - return fileSystemTypes.copy() - -def getUsableLinuxFs(): - rc = [] - for fsType in fileSystemTypes.keys(): - if fileSystemTypes[fsType].isMountable() and \ - fileSystemTypes[fsType].isLinuxNativeFS(): - rc.append(fsType) - - # make sure the default is first in the list, kind of ugly - default = fileSystemTypeGetDefault() - defaultName = default.getName() - if defaultName in rc: - del rc[rc.index(defaultName)] - rc.insert(0, defaultName) - return rc - -def devify(device): - if device in ["proc", "devpts", "sysfs", "tmpfs"] or device.find(":") != -1: - return device - elif device == "sys": - return "sysfs" - elif device == "shm": - return "tmpfs" - elif device == "spufs": - return "spufs" - elif device != "none" and device[0] != '/': - return "/dev/" + device - else: - return device - -class FileSystemType: - kernelFilesystems = {} - lostAndFoundContext = None - - def __init__(self): - self.deviceArguments = {} - self.formattable = 0 - self.checked = 0 - self.name = "" - self.linuxnativefs = 0 - self.fusefs = 0 - self.partedFileSystemType = None - self.partedPartitionFlags = [] - self.maxSizeMB = 8 * 1024 * 1024 - self.supported = -1 - self.defaultOptions = "defaults" - self.migratetofs = None - self.extraFormatArgs = [] - self.maxLabelChars = 16 - self.packages = [] - self.needProgram = [] - self.resizable = False - self.supportsFsProfiles = False - self.fsProfileSpecifier = None - self.fsprofile = None - self.bootable = False - - def createLabel(self, mountpoint, maxLabelChars, kslabel = None): - # If a label was specified in the kickstart file, return that as the - # label. - if kslabel: - return kslabel - - if len(mountpoint) > maxLabelChars: - return mountpoint[0:maxLabelChars] - else: - return mountpoint - - def isBootable(self): - return self.bootable - - def isResizable(self): - return self.resizable - def resize(self, entry, size, progress, chroot='/'): - pass - def getMinimumSize(self, device): - log.warning("Unable to determinine minimal size for %s", device) - return 1 - - def isKernelFS(self): - """Returns True if this is an in-kernel pseudo-filesystem.""" - return False - - def mount(self, device, mountpoint, readOnly=0, bindMount=0, - instroot=""): - if not self.isMountable(): - return - iutil.mkdirChain("%s/%s" %(instroot, mountpoint)) - if flags.selinux: - ret = isys.resetFileContext(mountpoint, instroot) - log.info("set SELinux context for mountpoint %s to %s" %(mountpoint, ret)) - log.debug("mounting %s on %s/%s as %s" %(device, instroot, - mountpoint, self.getMountName())) - isys.mount(device, "%s/%s" %(instroot, mountpoint), - fstype = self.getMountName(), - readOnly = readOnly, bindMount = bindMount, - options = self.defaultOptions) - - if flags.selinux: - ret = isys.resetFileContext(mountpoint, instroot) - log.info("set SELinux context for newly mounted filesystem root at %s to %s" %(mountpoint, ret)) - if FileSystemType.lostAndFoundContext is None: - FileSystemType.lostAndFoundContext = \ - isys.matchPathContext("/lost+found") - isys.setFileContext("%s/lost+found" % (mountpoint,), - FileSystemType.lostAndFoundContext, instroot) - - def umount(self, device, path): - isys.umount(path, removeDir = 0) - - def getName(self, quoted = 0): - """Return the name of the filesystem. Set quoted to 1 if this - should be quoted (ie, it's not for display).""" - if quoted: - if self.name.find(" ") != -1: - return "\"%s\"" %(self.name,) - return self.name - - def getMountName(self, quoted = 0): - return self.getName(quoted) - - def getNeededPackages(self): - return self.packages - - def registerDeviceArgumentFunction(self, klass, function): - self.deviceArguments[klass] = function - - def formatDevice(self, entry, progress, chroot='/'): - if self.isFormattable(): - raise RuntimeError, "formatDevice method not defined" - - def migrateFileSystem(self, device, message, chroot='/'): - if self.isMigratable(): - raise RuntimeError, "migrateFileSystem method not defined" - - def labelDevice(self, entry, chroot): - pass - - def clobberDevice(self, entry, chroot): - pass - - def isFormattable(self): - return self.formattable - - def isLinuxNativeFS(self): - return self.linuxnativefs - - def setFsProfile(self, fsprofile=None): - if not self.supportsFsProfiles: - raise RuntimeError, "%s does not support profiles" % (self,) - self.fsprofile = fsprofile - - def getFsProfileArgs(self): - if not self.supportsFsProfiles: - raise RuntimeError, "%s does not support profiles" % (self,) - args = None - if self.fsprofile: - args = [] - if self.fsProfileSpecifier: - args.extend(self.fsProfileSpecifier) - args.extend(self.fsprofile) - return args - - def readProcFilesystems(self): - f = open("/proc/filesystems", 'r') - if not f: - pass - lines = f.readlines() - for line in lines: - fields = string.split(line) - if fields[0] == "nodev": - fsystem = fields[1] - else: - fsystem = fields[0] - FileSystemType.kernelFilesystems[fsystem] = None - - def isMountable(self): - if not FileSystemType.kernelFilesystems: - self.readProcFilesystems() - if self.fusefs: - return FileSystemType.kernelFilesystems.has_key("fuse") - - return FileSystemType.kernelFilesystems.has_key(self.getMountName()) or self.getName() == "auto" - - def isSupported(self): - # check to ensure we have the binaries they need - for p in self.needProgram: - if len(filter(lambda d: os.path.exists("%s/%s" %(d, p)), - os.environ["PATH"].split(":"))) == 0: - return False - - if self.supported == -1: - return self.isMountable() - return self.supported - - def isChecked(self): - return self.checked - - def getDeviceArgs(self, device): - deviceArgsFunction = self.deviceArguments.get(device.__class__) - if not deviceArgsFunction: - return [] - return deviceArgsFunction(device) - - def getPartedFileSystemType(self): - return self.partedFileSystemType - - def getPartedPartitionFlags(self): - return self.partedPartitionFlags - - # note that this returns the maximum size of a filesystem in megabytes - def getMaxSizeMB(self): - return self.maxSizeMB - - def getDefaultOptions(self, mountpoint): - return self.defaultOptions - - def getMigratableFSTargets(self): - retval = [] - if not self.migratetofs: - return retval - - for fs in self.migratetofs: - if fileSystemTypeGet(fs).isSupported(): - retval.append(fs) - - return retval - - def isMigratable(self): - if len(self.getMigratableFSTargets()) > 0: - return 1 - else: - return 0 - - -class reiserfsFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = parted.fileSystemType["reiserfs"] - self.formattable = 1 - self.checked = 1 - self.linuxnativefs = 1 - self.bootable = True - # this is totally, 100% unsupported. Boot with "linux reiserfs" - # at the boot: prompt will let you make new reiserfs filesystems - # in the installer. Bugs filed when you use this will be closed - # WONTFIX. - if flags.cmdline.has_key("reiserfs"): - self.supported = -1 - else: - self.supported = 0 - - self.name = "reiserfs" - self.packages = [ "reiserfs-utils" ] - self.needProgram = [ "mkreiserfs", "reiserfstune" ] - - self.maxSizeMB = 8 * 1024 * 1024 - - def formatDevice(self, entry, progress, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - - p = os.pipe() - os.write(p[1], "y\n") - os.close(p[1]) - - rc = iutil.execWithRedirect("mkreiserfs", - [devicePath], - stdin = p[0], - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - - if rc: - raise SystemError - - def labelDevice(self, entry, chroot): - devicePath = entry.device.setupDevice(chroot) - label = self.createLabel(entry.mountpoint, self.maxLabelChars, - kslabel = entry.label) - rc = iutil.execWithRedirect("reiserfstune", - ["--label", label, devicePath], - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - if rc: - raise SystemError - entry.setLabel(label) - -fileSystemTypeRegister(reiserfsFileSystem()) - -class xfsFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = parted.fileSystemType["xfs"] - self.formattable = 1 - self.checked = 1 - self.linuxnativefs = 1 - self.name = "xfs" - self.maxSizeMB = 16 * 1024 * 1024 - self.maxLabelChars = 12 - self.supported = -1 - if not os.path.exists("/sbin/mkfs.xfs") and not os.path.exists("/usr/sbin/mkfs.xfs") and not os.path.exists("/usr/sbin/xfs_admin"): - self.supported = 0 - - self.packages = [ "xfsprogs" ] - self.needProgram = [ "mkfs.xfs", "xfs_admin" ] - - def formatDevice(self, entry, progress, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - - rc = iutil.execWithRedirect("mkfs.xfs", ["-f", devicePath], - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - - if rc: - raise SystemError - - def labelDevice(self, entry, chroot): - devicePath = entry.device.setupDevice(chroot) - label = self.createLabel(entry.mountpoint, self.maxLabelChars, - kslabel = entry.label) - rc = iutil.execWithRedirect("xfs_admin", - ["-L", label, devicePath], - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - if rc: - raise SystemError - entry.setLabel(label) - -fileSystemTypeRegister(xfsFileSystem()) - -class jfsFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = parted.fileSystemType["jfs"] - self.formattable = 1 - self.checked = 1 - self.linuxnativefs = 1 - self.maxLabelChars = 16 - self.bootable = True - # this is totally, 100% unsupported. Boot with "linux jfs" - # at the boot: prompt will let you make new reiserfs filesystems - # in the installer. Bugs filed when you use this will be closed - # WONTFIX. - if flags.cmdline.has_key("jfs"): - self.supported = -1 - else: - self.supported = 0 - - self.name = "jfs" - self.packages = [ "jfsutils" ] - self.needProgram = [ "mkfs.jfs", "jfs_tune" ] - - self.maxSizeMB = 8 * 1024 * 1024 - - def labelDevice(self, entry, chroot): - devicePath = entry.device.setupDevice(chroot) - label = self.createLabel(entry.mountpoint, self.maxLabelChars, - kslabel = entry.label) - rc = iutil.execWithRedirect("jfs_tune", - ["-L", label, devicePath], - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - if rc: - raise SystemError - entry.setLabel(label) - - def formatDevice(self, entry, progress, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - - rc = iutil.execWithRedirect("mkfs.jfs", - ["-q", devicePath], - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - - if rc: - raise SystemError - -fileSystemTypeRegister(jfsFileSystem()) - -class gfs2FileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = None - self.formattable = 1 - self.checked = 1 - self.linuxnativefs = 1 - if flags.cmdline.has_key("gfs2"): - self.supported = -1 - else: - self.supported = 0 - - self.name = "gfs2" - self.packages = [ "gfs2-utils" ] - self.needProgram = [ "mkfs.gfs2" ] - - self.maxSizeMB = 8 * 1024 * 1024 - - def formatDevice(self, entry, progress, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - rc = iutil.execWithRedirect("mkfs.gfs2", - ["-j", "1", "-p", "lock_nolock", - "-O", devicePath], - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - - if rc: - raise SystemError - -fileSystemTypeRegister(gfs2FileSystem()) - -class extFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = None - self.formattable = 1 - self.checked = 1 - self.linuxnativefs = 1 - self.maxSizeMB = 8 * 1024 * 1024 - self.packages = [ "e2fsprogs" ] - self.supportsFsProfiles = True - self.fsProfileSpecifier = "-T" - self.resizable = True - self.bootable = True - - def resize(self, entry, size, progress, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - - log.info("checking %s prior to resize" %(devicePath,)) - w = None - if progress: - w = progress(_("Checking"), - _("Checking filesystem on %s...") %(devicePath), - 100, pulse = True) - - rc = iutil.execWithPulseProgress("e2fsck", ["-f", "-p", "-C", "0", devicePath], - stdout="/tmp/resize.out", - stderr="/tmp/resize.out", - progress = w) - if rc >= 4: - raise ResizeError, ("Check of %s failed: %s" %(devicePath, rc), devicePath) - if progress: - w.pop() - w = progress(_("Resizing"), - _("Resizing filesystem on %s...") %(devicePath), - 100, pulse = True) - - log.info("resizing %s" %(devicePath,)) - rc = iutil.execWithPulseProgress("resize2fs", - ["-p", devicePath, "%sM" %(size,)], - stdout="/tmp/resize.out", - stderr="/tmp/resize.out", - progress = w) - if progress: - w.pop() - if rc: - raise ResizeError, ("Resize of %s failed: %s" %(devicePath, rc), devicePath) - - def getMinimumSize(self, device): - """Return the minimum filesystem size in megabytes""" - devicePath = "/dev/%s" % (device,) - - # FIXME: it'd be nice if we didn't have to parse this out ourselves - buf = iutil.execWithCapture("dumpe2fs", - ["-h", devicePath], - stderr = "/dev/tty5") - blocks = free = bs = 0 - for l in buf.split("\n"): - if l.startswith("Free blocks"): - try: - free = l.split()[2] - free = int(free) - except Exception, e: - log.warning("error determining free blocks on %s: %s" %(devicePath, e)) - free = 0 - elif l.startswith("Block size"): - try: - bs = l.split()[2] - bs = int(bs) - except Exception, e: - log.warning("error determining block size of %s: %s" %(devicePath, e)) - bs = 0 - elif l.startswith("Block count"): - try: - blocks = l.split()[2] - blocks = int(blocks) - except Exception, e: - log.warning("error determining block count of %s: %s" %(devicePath, e)) - blocks = 0 - - if free == 0 or bs == 0: - log.warning("Unable to determinine minimal size for %s", devicePath) - return 1 - - used = math.ceil((blocks - free) * bs / 1024.0 / 1024.0) - log.info("used size of %s is %s" %(devicePath, used)) - # FIXME: should we bump this beyond the absolute minimum? - return used - - def labelDevice(self, entry, chroot): - devicePath = entry.device.setupDevice(chroot) - label = self.createLabel(entry.mountpoint, self.maxLabelChars, - kslabel = entry.label) - - rc = iutil.execWithRedirect("e2label", - [devicePath, label], - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - if rc: - raise SystemError - entry.setLabel(label) - - def formatDevice(self, entry, progress, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - devArgs = self.getDeviceArgs(entry.device) - args = [ "mke2fs", devicePath ] - - fsProfileArgs = self.getFsProfileArgs() - if fsProfileArgs: - args.extend(fsProfileArgs) - args.extend(devArgs) - args.extend(self.extraFormatArgs) - - log.info("Format command: %s\n" % str(args)) - - rc = ext2FormatFilesystem(args, "/dev/tty5", - progress, - entry.mountpoint) - if rc: - raise SystemError - - def clobberDevice(self, entry, chroot): - device = entry.device.setupDevice(chroot) - isys.ext2Clobber(device) - - # this is only for ext3 filesystems, but migration is a method - # of the ext2 fstype, so it needs to be here. FIXME should be moved - def setExt3Options(self, entry, message, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - - # if no journal, don't turn off the fsck - if not isys.ext2HasJournal(devicePath): - return - - rc = iutil.execWithRedirect("tune2fs", - ["-c0", "-i0", - "-ouser_xattr,acl", devicePath], - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - -class ext2FileSystem(extFileSystem): - def __init__(self): - extFileSystem.__init__(self) - self.name = "ext2" - self.partedFileSystemType = parted.fileSystemType["ext2"] - self.migratetofs = ['ext3'] - - def migrateFileSystem(self, entry, message, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - - if not entry.fsystem or not entry.origfsystem: - raise RuntimeError, ("Trying to migrate fs w/o fsystem or " - "origfsystem set") - if entry.fsystem.getName() != "ext3": - raise RuntimeError, ("Trying to migrate ext2 to something other " - "than ext3") - - # if journal already exists skip - if isys.ext2HasJournal(devicePath): - log.info("Skipping migration of %s, has a journal already.\n" % devicePath) - return - - rc = iutil.execWithRedirect("tune2fs", - ["-j", devicePath ], - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - - if rc: - raise SystemError - - # XXX this should never happen, but appears to have done - # so several times based on reports in bugzilla. - # At least we can avoid leaving them with a system which won't boot - if not isys.ext2HasJournal(devicePath): - log.warning("Migration of %s attempted but no journal exists after " - "running tune2fs.\n" % (devicePath)) - if message: - rc = message(_("Error"), - _("An error occurred migrating %s to ext3. It is " - "possible to continue without migrating this " - "file system if desired.\n\n" - "Would you like to continue without migrating %s?") - % (devicePath, devicePath), type = "yesno") - if rc == 0: - sys.exit(0) - entry.fsystem = entry.origfsystem - else: - extFileSystem.setExt3Options(self, entry, message, chroot) - - -fileSystemTypeRegister(ext2FileSystem()) - -class ext3FileSystem(extFileSystem): - def __init__(self): - extFileSystem.__init__(self) - self.name = "ext3" - self.extraFormatArgs = [ "-t", "ext3" ] - self.partedFileSystemType = parted.fileSystemType["ext3"] - if flags.cmdline.has_key("ext4migrate"): - self.migratetofs = ['ext4'] - - def formatDevice(self, entry, progress, chroot='/'): - extFileSystem.formatDevice(self, entry, progress, chroot) - extFileSystem.setExt3Options(self, entry, progress, chroot) - - def migrateFileSystem(self, entry, message, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - - if not entry.fsystem or not entry.origfsystem: - raise RuntimeError, ("Trying to migrate fs w/o fsystem or " - "origfsystem set") - if entry.fsystem.getName() != "ext4": - raise RuntimeError, ("Trying to migrate ext3 to something other " - "than ext4") - - rc = iutil.execWithRedirect("tune2fs", ["-O", "extents", devicePath], - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - - if rc: - raise SystemError - -fileSystemTypeRegister(ext3FileSystem()) - -class ext4FileSystem(extFileSystem): - def __init__(self): - extFileSystem.__init__(self) - self.name = "ext4" - self.partedFileSystemType = parted.fileSystemType["ext3"] - self.extraFormatArgs = [ "-t", "ext4" ] - self.bootable = False - - def formatDevice(self, entry, progress, chroot='/'): - extFileSystem.formatDevice(self, entry, progress, chroot) - extFileSystem.setExt3Options(self, entry, progress, chroot) - -fileSystemTypeRegister(ext4FileSystem()) - -class btrfsFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.formattable = 1 - self.checked = 1 - self.linuxnativefs = 1 - self.bootable = False - self.maxLabelChars = 256 - # Wow, you must be brave! - # this is totally, 100% unsupported. Boot with "linux btrfs" - # at the boot: prompt will let you make new btrfs filesystems - # in the installer. - if flags.cmdline.has_key("icantbelieveitsnotbtr"): - self.supported = -1 - else: - self.supported = 0 - - self.name = "btrfs" - self.packages = [ "btrfs-progs" ] - self.needProgram = [ "mkfs.btrfs", "btrfsctl" ] - - # Bigger, really, depending on machine - self.maxSizeMB = 16 * 1024 * 1024 - - # We'll sneakily label it here, too. - def formatDevice(self, entry, progress, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - label = self.createLabel(entry.mountpoint, self.maxLabelChars, - kslabel = entry.label) - - rc = iutil.execWithRedirect("mkfs.btrfs", - ["-L", label, devicePath], - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - - if rc: - raise SystemError - entry.setLabel(label) - def labelDevice(self, entry, chroot): - # We did this on the initial format; no standalone labeler yet - pass - - def resize(self, entry, size, progress, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - log.info("resizing %s" %(devicePath,)) - - w = None - if progress: - w = progress(_("Resizing"), - _("Resizing filesystem on %s...") %(devicePath), - 100, pulse = True) - - rc = iutil.execWithPulseProgress("btrfsctl", - ["-r", "%sM" %(size,), devicePath], - stdout="/tmp/resize.out", - stderr="/tmp/resize.out", - progress = w) - if progress: - w.pop() - if rc: - raise ResizeError, ("Resize of %s failed: %s" %(devicePath, rc), devicePath) - - -fileSystemTypeRegister(btrfsFileSystem()) - -class raidMemberDummyFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = parted.fileSystemType["ext2"] - self.partedPartitionFlags = [ parted.PARTITION_RAID ] - self.formattable = 1 - self.checked = 0 - self.linuxnativefs = 1 - self.name = "software RAID" - self.maxSizeMB = 8 * 1024 * 1024 - self.supported = 1 - - if len(raid.availRaidLevels) == 0: - self.supported = 0 - - self.packages = [ "mdadm" ] - - def formatDevice(self, entry, progress, chroot='/'): - # mkraid did all we need to format this partition... - pass - -fileSystemTypeRegister(raidMemberDummyFileSystem()) - -class lvmPhysicalVolumeDummyFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = parted.fileSystemType["ext2"] - self.partedPartitionFlags = [ parted.PARTITION_LVM ] - self.formattable = 1 - self.checked = 0 - self.linuxnativefs = 1 - self.name = "physical volume (LVM)" - self.maxSizeMB = 8 * 1024 * 1024 - self.supported = 1 - self.packages = [ "lvm2" ] - - def isMountable(self): - return 0 - - def formatDevice(self, entry, progress, chroot='/'): - # already done by the pvcreate during volume creation - pass - -fileSystemTypeRegister(lvmPhysicalVolumeDummyFileSystem()) - -class lvmVolumeGroupDummyFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = parted.fileSystemType["ext2"] - self.formattable = 1 - self.checked = 0 - self.linuxnativefs = 0 - self.name = "volume group (LVM)" - self.supported = 0 - self.maxSizeMB = 8 * 1024 * 1024 - self.packages = [ "lvm2" ] - - def isMountable(self): - return 0 - - def formatDevice(self, entry, progress, chroot='/'): - # the vgcreate already did this - pass - -fileSystemTypeRegister(lvmVolumeGroupDummyFileSystem()) - -class swapFileSystem(FileSystemType): - enabledSwaps = {} - - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = parted.fileSystemType["linux-swap"] - self.formattable = 1 - self.name = "swap" - self.maxSizeMB = 8 * 1024 * 1024 - self.linuxnativefs = 1 - self.supported = 1 - self.maxLabelChars = 15 - - def mount(self, device, mountpoint, readOnly=0, bindMount=0, - instroot = None): - pagesize = resource.getpagesize() - buf = None - if pagesize > 2048: - num = pagesize - else: - num = 2048 - try: - fd = os.open(device, os.O_RDONLY) - buf = os.read(fd, num) - except: - pass - finally: - try: - os.close(fd) - except: - pass - - if buf is not None and len(buf) == pagesize: - sig = buf[pagesize - 10:] - if sig == 'SWAP-SPACE': - raise OldSwapError - if sig == 'S1SUSPEND\x00' or sig == 'S2SUSPEND\x00': - raise SuspendError - - isys.swapon (device) - - def umount(self, device, path): - if os.path.exists("/dev/" + device.device): - swapFile = os.path.realpath("/dev/" + device.device) - else: - # path is something like /mnt/sysimage/swap, which is not very - # useful. But since we don't have instPath anywhere else, so - # we have to pull it out of path and add the real swap file to - # the end of it. - swapFile = os.path.realpath(os.path.dirname(path) + "/" + device.device) - - try: - iutil.execWithRedirect("swapoff", [swapFile], stdout="/dev/tty5", - stderr="/dev/tty5", searchPath=1) - except: - raise RuntimeError, "unable to turn off swap" - - def formatDevice(self, entry, progress, chroot='/'): - file = entry.device.setupDevice(chroot) - rc = iutil.execWithRedirect ("mkswap", - ['-v1', file], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - searchPath = 1) - if rc: - raise SystemError - - def labelDevice(self, entry, chroot): - file = entry.device.setupDevice(chroot) - devName = entry.device.getDevice() - # we'll keep the SWAP-* naming for all devs but Compaq SMART2 - # nodes (#176074) - if devName[0:6] == "cciss/": - swapLabel = "SW-%s" % (devName) - elif devName.startswith("mapper/"): - swapLabel = "SWAP-%s" % (devName[7:],) - else: - swapLabel = "SWAP-%s" % (devName) - label = self.createLabel(swapLabel, self.maxLabelChars) - rc = iutil.execWithRedirect ("mkswap", - ['-v1', "-L", label, file], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - searchPath = 1) - if rc: - raise SystemError - entry.setLabel(label) - - def clobberDevice(self, entry, chroot): - pagesize = resource.getpagesize() - dev = entry.device.setupDevice(chroot) - try: - fd = os.open(dev, os.O_RDWR) - buf = "\0x00" * pagesize - os.write(fd, buf) - except: - pass - finally: - try: - os.close(fd) - except: - pass - -fileSystemTypeRegister(swapFileSystem()) - -class FATFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = parted.fileSystemType["fat32"] - self.formattable = 1 - self.checked = 0 - self.maxSizeMB = 1024 * 1024 - self.name = "vfat" - self.packages = [ "dosfstools" ] - self.defaultOptions = "umask=0077,shortname=winnt" - - def formatDevice(self, entry, progress, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - devArgs = self.getDeviceArgs(entry.device) - args = [ devicePath ] - args.extend(devArgs) - - rc = iutil.execWithRedirect("mkdosfs", args, - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - if rc: - raise SystemError - - def labelDevice(self, entry, chroot): - devicePath = entry.device.setupDevice(chroot) - label = self.createLabel(entry.mountpoint, self.maxLabelChars, - kslabel = entry.label) - - rc = iutil.execWithRedirect("dosfslabel", - [devicePath, label], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - searchPath = 1) - if rc: - msg = iutil.execWithCapture("dosfslabel", [devicePath], - stderr="/dev/tty5") - raise SystemError, "dosfslabel failed on device %s: %s" % (devicePath, msg) - - newLabel = iutil.execWithCapture("dosfslabel", [devicePath], - stderr = "/dev/tty5") - newLabel = newLabel.strip() - if label != newLabel: - raise SystemError, "dosfslabel failed on device %s" % (devicePath,) - entry.setLabel(label) - -fileSystemTypeRegister(FATFileSystem()) - -class EFIFileSystem(FATFileSystem): - def __init__(self): - FATFileSystem.__init__(self) - self.name = "efi" - self.partedPartitionFlags = [ parted.PARTITION_BOOT ] - self.maxSizeMB = 256 - self.defaultOptions = "umask=0077,shortname=winnt" - self.bootable = True - if not iutil.isEfi(): - self.supported = 0 - - def getMountName(self, quoted = 0): - return "vfat" - - def formatDevice(self, entry, progress, chroot='/'): - FATFileSystem.formatDevice(self, entry, progress, chroot) - - # XXX need to set the name in GPT - # entry.device.something.part.set_name("EFI System Partition") - devicePath = entry.device.setupDevice(chroot) - -fileSystemTypeRegister(EFIFileSystem()) - -class NTFSFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = parted.fileSystemType["ntfs"] - self.formattable = 0 - self.checked = 0 - self.name = "ntfs" - if len(filter(lambda d: os.path.exists("%s/ntfsresize" %(d,)), - os.environ["PATH"].split(":"))) > 0: - self.resizable = True - if len(filter(lambda d: os.path.exists("%s/mount.ntfs-3g" %(d,)), - os.environ["PATH"].split(":"))) > 0: - self.fusefs = 1 - - def resize(self, entry, size, progress, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - log.info("resizing %s to %sM" %(devicePath, size)) - w = None - if progress: - w = progress(_("Resizing"), - _("Resizing filesystem on %s...") %(devicePath), - 100, pulse = True) - - p = os.pipe() - os.write(p[1], "y\n") - os.close(p[1]) - - # FIXME: we should call ntfsresize -c to ensure that we can resize - # before starting the operation - - rc = iutil.execWithPulseProgress("ntfsresize", ["-v", - "-s", "%sM" %(size,), - devicePath], - stdin = p[0], - stdout = "/tmp/resize.out", - stderr = "/tmp/resize.out", - progress = w) - if progress: - w.pop() - if rc: - raise ResizeError, ("Resize of %s failed" %(devicePath,), devicePath) - - def getMinimumSize(self, device): - """Return the minimum filesystem size in megabytes""" - devicePath = "/dev/%s" % (device,) - - buf = iutil.execWithCapture("ntfsresize", ["-m", devicePath], - stderr = "/dev/tty5") - for l in buf.split("\n"): - if not l.startswith("Minsize"): - continue - try: - min = l.split(":")[1].strip() - return int(min) + 250 - except Exception, e: - log.warning("Unable to parse output for minimum size on %s: %s" %(device, e)) - - log.warning("Unable to discover minimum size of filesystem on %s" %(device,)) - return 1 - - -fileSystemTypeRegister(NTFSFileSystem()) - -class hfsFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = parted.fileSystemType["hfs"] - self.formattable = 1 - self.checked = 0 - self.name = "hfs" - self.supported = 0 - self.needProgram = [ "hformat" ] - - def isMountable(self): - return 0 - - def formatDevice(self, entry, progress, chroot='/'): - devicePath = entry.device.setupDevice(chroot) - devArgs = self.getDeviceArgs(entry.device) - args = [ devicePath ] - args.extend(devArgs) - - rc = iutil.execWithRedirect("hformat", args, - stdout = "/dev/tty5", - stderr = "/dev/tty5", searchPath = 1) - if rc: - raise SystemError - -fileSystemTypeRegister(hfsFileSystem()) - -class HfsPlusFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = parted.fileSystemType["hfs+"] - self.formattable = 0 - self.checked = 0 - self.name = "hfs+" - -fileSystemTypeRegister(HfsPlusFileSystem()) - -class applebootstrapFileSystem(hfsFileSystem): - def __init__(self): - hfsFileSystem.__init__(self) - self.partedPartitionFlags = [ parted.PARTITION_BOOT ] - self.maxSizeMB = 1 - self.name = "Apple Bootstrap" - self.bootable = True - if iutil.getPPCMacGen() == "NewWorld": - self.linuxnativefs = 1 - self.supported = 1 - else: - self.linuxnativefs = 0 - self.supported = 0 - -fileSystemTypeRegister(applebootstrapFileSystem()) - -class prepbootFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.partedFileSystemType = None - self.partedPartitionFlags = [ parted.PARTITION_BOOT, parted.PARTITION_PREP ] - self.checked = 0 - self.name = "PPC PReP Boot" - self.maxSizeMB = 10 - self.bootable = True - - if iutil.getPPCMachine() == "iSeries": - self.maxSizeMB = 64 - - # supported for use on the pseries - if (iutil.getPPCMachine() == "pSeries" or - iutil.getPPCMachine() == "iSeries"): - self.linuxnativefs = 1 - self.supported = 1 - self.formattable = 1 - else: - self.linuxnativefs = 0 - self.supported = 0 - self.formattable = 0 - - def formatDevice(self, entry, progress, chroot='/'): - return - -fileSystemTypeRegister(prepbootFileSystem()) - -class networkFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.formattable = 0 - self.checked = 0 - self.name = "nfs" - - def isMountable(self): - return 0 - -fileSystemTypeRegister(networkFileSystem()) - -class nfsv4FileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.formattable = 0 - self.checked = 0 - self.name = "nfs4" - - def isMountable(self): - return 0 - -fileSystemTypeRegister(nfsv4FileSystem()) - -class ForeignFileSystem(FileSystemType): - def __init__(self): - FileSystemType.__init__(self) - self.formattable = 0 - self.checked = 0 - self.name = "foreign" - - def formatDevice(self, entry, progress, chroot='/'): - return - -fileSystemTypeRegister(ForeignFileSystem()) - -class PseudoFileSystem(FileSystemType): - def __init__(self, name): - FileSystemType.__init__(self) - self.formattable = 0 - self.checked = 0 - self.name = name - self.supported = 0 - - def isKernelFS(self): - return True - -class SpuFileSystem(PseudoFileSystem): - def __init__(self): - PseudoFileSystem.__init__(self, "spufs") - -fileSystemTypeRegister(SpuFileSystem()) - -class ProcFileSystem(PseudoFileSystem): - def __init__(self): - PseudoFileSystem.__init__(self, "proc") - -fileSystemTypeRegister(ProcFileSystem()) - -class SysfsFileSystem(PseudoFileSystem): - def __init__(self): - PseudoFileSystem.__init__(self, "sysfs") - -fileSystemTypeRegister(SysfsFileSystem()) - -class SelinuxfsFileSystem(PseudoFileSystem): - def __init__(self): - PseudoFileSystem.__init__(self, "selinuxfs") - -fileSystemTypeRegister(SelinuxfsFileSystem()) - -class DevptsFileSystem(PseudoFileSystem): - def __init__(self): - PseudoFileSystem.__init__(self, "devpts") - self.defaultOptions = "gid=5,mode=620" - - def isMountable(self): - return 0 - -fileSystemTypeRegister(DevptsFileSystem()) - -class DevshmFileSystem(PseudoFileSystem): - def __init__(self): - PseudoFileSystem.__init__(self, "tmpfs") - - def isMountable(self): - return 0 - -fileSystemTypeRegister(DevshmFileSystem()) - -class AutoFileSystem(PseudoFileSystem): - def __init__(self): - PseudoFileSystem.__init__(self, "auto") - - def mount(self, device, mountpoint, readOnly=0, bindMount=0, - instroot = None): - errNum = 0 - errMsg = "cannot mount auto filesystem on %s of this type" % device - - if not self.isMountable(): - return - iutil.mkdirChain("%s/%s" %(instroot, mountpoint)) - if flags.selinux: - ret = isys.resetFileContext(mountpoint, instroot) - log.info("set SELinux context for mountpoint %s to %s" %(mountpoint, ret)) - - fs = isys.readFSType(device) - if fs is not None: - try: - isys.mount (device, mountpoint, fstype = fs, readOnly = - readOnly, bindMount = bindMount) - return - except SystemError, (num, msg): - errNum = num - errMsg = msg - - raise SystemError (errNum, errMsg) - - def umount(self, device, path): - isys.umount(path, removeDir = 0) - -fileSystemTypeRegister(AutoFileSystem()) - -class BindFileSystem(PseudoFileSystem): - def __init__(self): - PseudoFileSystem.__init__(self, "bind") - - def isMountable(self): - return 1 - -fileSystemTypeRegister(BindFileSystem()) - -class FileSystemSet: - def __init__(self): - self.messageWindow = None - self.progressWindow = None - self.waitWindow = None - self.mountcount = 0 - self.migratedfs = 0 - self.reset() - self.volumesCreated = 0 - - def isActive(self): - return self.mountcount != 0 - - def registerMessageWindow(self, method): - self.messageWindow = method - - def registerProgressWindow(self, method): - self.progressWindow = method - - def registerWaitWindow(self, method): - self.waitWindow = method - - def reset (self): - self.entries = [] - proc = FileSystemSetEntry(Device(device="proc"), '/proc', - fileSystemTypeGet("proc")) - self.add(proc) - sys = FileSystemSetEntry(Device(device="sys"), '/sys', - fileSystemTypeGet("sysfs")) - self.add(sys) - pts = FileSystemSetEntry(Device(device="devpts"), '/dev/pts', - fileSystemTypeGet("devpts"), "gid=5,mode=620") - self.add(pts) - shm = FileSystemSetEntry(Device(device="shm"), '/dev/shm', - fileSystemTypeGet("tmpfs")) - self.add(shm) - - if iutil.isCell(): - spu = FileSystemSetEntry(Device(device="spufs"), '/spu', - fileSystemTypeGet("spufs")) - self.add(spu) - - def verify (self): - for entry in self.entries: - if type(entry.__dict__) != type({}): - raise RuntimeError, "fsset internals inconsistent" - - def add (self, newEntry): - # Should object A be sorted after object B? Take mountpoints and - # device names into account so bind mounts are sorted correctly. - def comesAfter (a, b): - mntA = a.mountpoint - mntB = b.mountpoint - devA = a.device.getDevice() - devB = b.device.getDevice() - - if not mntB: - return False - if mntA and mntA != mntB and mntA.startswith(mntB): - return True - if devA and devA != mntB and devA.startswith(mntB): - return True - return False - - def samePseudo (a, b): - return isinstance(a.fsystem, PseudoFileSystem) and isinstance (b.fsystem, PseudoFileSystem) and \ - not isinstance (a.fsystem, BindFileSystem) and not isinstance (b.fsystem, BindFileSystem) and \ - a.fsystem.getName() == b.fsystem.getName() - - def sameEntry (a, b): - return a.device.getDevice() == b.device.getDevice() and a.mountpoint == b.mountpoint - - # Remove preexisting duplicate entries - pseudo filesystems are - # duplicate if they have the same filesystem type as an existing one. - # Otherwise, they have to have the same device and mount point - # (required to check for bind mounts). - for existing in self.entries: - if samePseudo (newEntry, existing) or sameEntry (newEntry, existing): - self.remove(existing) - - # XXX debuggin' -## log.info ("fsset at %s\n" -## "adding entry for %s\n" -## "entry object %s, class __dict__ is %s", -## self, entry.mountpoint, entry, -## isys.printObject(entry.__dict__)) - - insertAt = 0 - - # Special case for /. - if newEntry.mountpoint == "/": - self.entries.insert(insertAt, newEntry) - return - - # doesn't matter where these get added, so just put them at the end - if not newEntry.mountpoint or not newEntry.mountpoint.startswith("/") or self.entries == []: - self.entries.append(newEntry) - return - - for entry in self.entries: - if comesAfter(newEntry, entry): - insertAt = self.entries.index(entry)+1 - - self.entries.insert(insertAt, newEntry) - - def remove (self, entry): - self.entries.remove(entry) - - def getEntryByMountPoint(self, mount): - for entry in self.entries: - if entry.mountpoint == mount: - return entry - return None - - def getEntryByDeviceName(self, dev): - for entry in self.entries: - if entry.device.getDevice() == dev: - return entry - - # getDevice() will return the mapped device if using LUKS - if entry.device.device == dev: - return entry - - return None - - def copy (self): - new = FileSystemSet() - for entry in self.entries: - new.add (entry) - return new - - def fstab (self): - format = "%-23s %-23s %-7s %-15s %d %d\n" - fstab = """ -# -# /etc/fstab -# Created by anaconda on %s -# -# Accessible filesystems, by reference, are maintained under '/dev/disk' -# See man pages fstab(5), findfs(8), mount(8) and/or vol_id(8) for more info -# -""" % time.asctime() - - for entry in self.entries: - if entry.mountpoint: - if entry.getUuid() and entry.device.doLabel is not None: - device = "UUID=%s" %(entry.getUuid(),) - elif entry.getLabel() and entry.device.doLabel is not None: - device = "LABEL=%s" % (entry.getLabel(),) - else: - device = devify(entry.device.getDevice()) - fstab = fstab + entry.device.getComment() - fstab = fstab + format % (device, entry.mountpoint, - entry.fsystem.getMountName(), - entry.getOptions(), entry.fsck, - entry.order) - return fstab - - def mtab (self): - format = "%s %s %s %s 0 0\n" - mtab = "" - for entry in self.entries: - if not entry.isMounted(): - continue - if entry.mountpoint: - # swap doesn't end up in the mtab - if entry.fsystem.getName() == "swap": - continue - options = entry.getOptions() - if options: - options = "rw," + options - else: - options = "rw" - mtab = mtab + format % (devify(entry.device.getDevice()), - entry.mountpoint, - entry.fsystem.getName(), - options) - return mtab - - def raidtab(self): - # set up raidtab... - raidtab = "" - for entry in self.entries: - if entry.device.getName() == "RAIDDevice": - raidtab = raidtab + entry.device.raidTab() - - return raidtab - - def mdadmConf(self): - """Make the mdadm.conf file with mdadm command. - - This creates a conf file with active arrays. In other words - the arrays that we don't want included must be inactive. - """ - activeArrays = iutil.execWithCapture("mdadm", ["--detail", "--scan"]) - if len(activeArrays) == 0: - return - - cf = """ -# mdadm.conf written out by anaconda -DEVICE partitions -MAILADDR root - -%s -""" % activeArrays - return cf - - def crypttab(self): - """set up /etc/crypttab""" - crypttab = "" - for entry in self.entries: - if entry.device.crypto: - crypttab += entry.device.crypto.crypttab() - - return crypttab - - def write (self, prefix): - f = open (prefix + "/etc/fstab", "w") - f.write (self.fstab()) - f.close () - - cf = self.mdadmConf() - - if cf: - f = open (prefix + "/etc/mdadm.conf", "w") - f.write (cf) - f.close () - - crypttab = self.crypttab() - if crypttab: - f = open(prefix + "/etc/crypttab", "w") - f.write(crypttab) - f.close() - - # touch mtab - open (prefix + "/etc/mtab", "w+") - f.close () - - def mkDevRoot(self, instPath): - root = self.getEntryByMountPoint("/") - dev = "%s/dev/%s" % (instPath, root.device.getDevice()) - if not os.path.exists("%s/dev/root" %(instPath,)) and os.path.exists(dev): - rdev = os.stat(dev).st_rdev - os.mknod("%s/dev/root" % (instPath,), stat.S_IFBLK | 0600, rdev) - - # return the "boot" device - def getBootDev(self): - mntDict = {} - bootDev = None - for entry in self.entries: - mntDict[entry.mountpoint] = entry - - # FIXME: this ppc stuff feels kind of crufty -- the abstraction - # here needs a little bit of work - if iutil.getPPCMacGen() == "NewWorld": - for entry in self.entries: - if entry.fsystem.getName() == "Apple Bootstrap": - bootDev = entry - elif (iutil.getPPCMachine() == "pSeries" or - iutil.getPPCMachine() == "iSeries"): - # we want the first prep partition or the first newly formatted one - bestprep = None - for entry in self.entries: - if ((entry.fsystem.getName() == "PPC PReP Boot") - and ((bestprep is None) or - ((bestprep.format == 0) and (entry.format == 1)))): - bestprep = entry - if bestprep: - bootDev = bestprep - elif iutil.isEfi(): - if mntDict.has_key("/boot/efi"): - bootDev = mntDict['/boot/efi'] - elif mntDict.has_key("/boot"): - bootDev = mntDict['/boot'] - elif mntDict.has_key("/"): - bootDev = mntDict['/'] - - return bootDev - - def bootloaderChoices(self, diskSet, bl): - ret = {} - bootDev = self.getBootDev() - - if bootDev is None: - log.warning("no boot device set") - return ret - - if iutil.isEfi(): - ret['boot'] = (bootDev.device, N_("EFI System Partition")) - return ret - - if bootDev.device.getName() == "RAIDDevice": - ret['boot'] = (bootDev.device, N_("RAID Device")) - ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) - return ret - - if iutil.getPPCMacGen() == "NewWorld": - ret['boot'] = (bootDev.device, N_("Apple Bootstrap")) - n = 1 - for entry in self.entries: - if ((entry.fsystem.getName() == "Apple Bootstrap") and ( - entry.device.getDevice() != bootDev.device)): - ret['boot%d' %(n,)] = (entry.device.getDevice(), - N_("Apple Bootstrap")) - n = n + 1 - return ret - elif (iutil.getPPCMachine() == "pSeries" or - iutil.getPPCMachine() == "iSeries"): - ret['boot'] = (bootDev.device, N_("PPC PReP Boot")) - return ret - - ret['boot'] = (bootDev.device, N_("First sector of boot partition")) - ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) - return ret - - # set active partition on disks - # if an active partition is set, leave it alone; if none set - # set either our boot partition or the first partition on the drive active - def setActive(self, diskset, requests): - bootDev = self.getBootDev() - - if bootDev is None: - return - - if bootDev.device.getName() != "RAIDDevice": - for request in requests: - if request.mountpoint == bootDev.mountpoint: - break - - for drive in request.drive: - part = diskset.disks[drive].getPartitionByPath("/dev/%s" % bootDev.device.device) - if part: - # on EFI systems, *only* /boot/efi should be marked bootable - # similarly, on pseries, we really only want the PReP partition - # active - if iutil.isEfi() or \ - iutil.getPPCMachine() in ("pSeries", "iSeries", "PMac") or \ - (iutil.isX86() and partedUtils.hasGptLabel(diskset, drive)): - if part and part.isFlagAvailable(parted.PARTITION_BOOT): - part.setFlag(parted.PARTITION_BOOT) - return - - for drive in diskset.disks.keys(): - foundActive = 0 - bootPart = None - if partedUtils.hasGptLabel(diskset, drive): - continue - disk = diskset.disks[drive] - for part in disk.partitions: - if not part.active: - continue - - if not part.isFlagAvailable(parted.PARTITION_BOOT): - foundActive = 1 - part = None - continue - - if part.getFlag(parted.PARTITION_BOOT): - foundActive = 1 - part = None - continue - - if not bootPart: - bootPart = part - - if part.getDeviceNodeName() == bootDev.device.device: - bootPart = part - - if bootPart and not foundActive: - bootPart.setFlag(parted.PARTITION_BOOT) - - if bootPart: - del bootPart - - def resizeFilesystems (self, diskset, chroot = '/', shrink = False, grow = False): - todo = [] - for entry in self.entries: - if not entry.fsystem or not entry.fsystem.isResizable(): - continue - if entry.fsystem.isFormattable() and entry.getFormat(): - continue - if entry.resizeTargetSize is None: - continue - if shrink and not (entry.resizeTargetSize < entry.resizeOrigSize): - continue - if grow and not (entry.resizeTargetSize > entry.resizeOrigSize): - continue - todo.append(entry) - if len(todo) == 0: - return - - # we have to have lvm activated to be able to do resizes of LVs - lvmActive = lvm.vgcheckactive() - devicesActive = diskset.devicesOpen - - if not devicesActive: - # should this not be diskset.openDevices() ? - diskset.startMPath() - diskset.startDmRaid() - diskset.startMdRaid() - - if not lvmActive: - lvm.vgscan() - lvm.vgactivate() - - for entry in todo: - entry.fsystem.resize(entry, entry.resizeTargetSize, - self.progressWindow, chroot) - if not lvmActive: - lvm.vgdeactivate() - - if not devicesActive: - # should this not be diskset.closeDevices() ? - diskset.stopMPath() - diskset.stopDmRaid() - diskset.stopMdRaid() - - def shrinkFilesystems (self, diskset, chroot): - self.resizeFilesystems(diskset, chroot, shrink = True) - def growFilesystems (self, diskset, chroot): - self.resizeFilesystems(diskset, chroot, grow = True) - - def formatSwap (self, chroot, forceFormat=False): - formatted = [] - notformatted = [] - - for entry in self.entries: - if (not entry.fsystem or not entry.fsystem.getName() == "swap" or - entry.isMounted()): - continue - if not entry.getFormat(): - if not forceFormat: - notformatted.append(entry) - continue - try: - self.formatEntry(entry, chroot) - formatted.append(entry) - except SystemError: - if self.messageWindow: - self.messageWindow(_("Error"), - _("An error occurred trying to " - "initialize swap on device %s. This " - "problem is serious, and the install " - "cannot continue.\n\n" - "Press to exit the installer.") - % (entry.device.getDevice(),)) - sys.exit(0) - - for entry in formatted: - try: - self.labelEntry(entry, chroot) - except SystemError: - # should be OK, fall back to by device - pass - - # find if there's a label on the ones we're not formatting - for entry in notformatted: - dev = entry.device.getDevice() - if not dev or dev == "none": - continue - try: - label = isys.readFSLabel(dev) - except: - continue - if label: - entry.setLabel(label) - - def turnOnSwap (self, chroot, upgrading=False): - def swapErrorDialog (msg, format_button_text, entry): - buttons = [_("Skip"), format_button_text, _("_Exit installer")] - ret = self.messageWindow(_("Error"), msg, type="custom", - custom_buttons=buttons, - custom_icon="warning") - if ret == 0: - self.entries.remove(entry) - elif ret == 1: - self.formatEntry(entry, chroot) - entry.mount(chroot) - self.mountcount = self.mountcount + 1 - else: - sys.exit(0) - - for entry in self.entries: - if (entry.fsystem and entry.fsystem.getName() == "swap" - and not entry.isMounted()): - try: - entry.mount(chroot) - self.mountcount = self.mountcount + 1 - except OldSwapError: - if self.messageWindow: - msg = _("The swap device:\n\n /dev/%s\n\n" - "is a version 0 Linux swap partition. If you " - "want to use this device, you must reformat as " - "a version 1 Linux swap partition. If you skip " - "it, the installer will ignore it during the " - "installation.") % (entry.device.getDevice()) - - swapErrorDialog(msg, _("Reformat"), entry) - except SuspendError: - if self.messageWindow: - if upgrading: - msg = _("The swap device:\n\n /dev/%s\n\n" - "in your /etc/fstab file is currently in " - "use as a software suspend partition, " - "which means your system is hibernating. " - "To perform an upgrade, please shut down " - "your system rather than hibernating it.") \ - % (entry.device.getDevice()) - else: - msg = _("The swap device:\n\n /dev/%s\n\n" - "in your /etc/fstab file is currently in " - "use as a software suspend partition, " - "which means your system is hibernating. " - "If you are performing a new install, " - "make sure the installer is set " - "to format all swap partitions.") \ - % (entry.device.getDevice()) - - # choose your own adventure swap partitions... - msg = msg + _("\n\nChoose Skip if you want the " - "installer to ignore this partition during " - "the upgrade. Choose Format to reformat " - "the partition as swap space.") - - swapErrorDialog(msg, _("Format"), entry) - else: - sys.exit(0) - except SystemError, (num, msg): - if self.messageWindow: - if upgrading and not entry.getLabel(): - err = _("Error enabling swap device %s: %s\n\n" - "Devices in /etc/fstab should be specified " - "by label, not by device name.\n\nPress " - "OK to exit the installer.") % (entry.device.getDevice(), msg) - elif upgrading: - err = _("Error enabling swap device %s: %s\n\n" - "The /etc/fstab on your upgrade partition " - "does not reference a valid swap " - "partition.\n\nPress OK to exit the " - "installer") % (entry.device.getDevice(), msg) - else: - err = _("Error enabling swap device %s: %s\n\n" - "This most likely means this swap " - "partition has not been initialized.\n\n" - "Press OK to exit the installer.") % (entry.device.getDevice(), msg) - - self.messageWindow(_("Error"), err) - sys.exit(0) - - def labelEntry(self, entry, chroot, ignoreExisting = False): - label = entry.device.getLabel() - if label and not ignoreExisting: - entry.setLabel(label) - entry.device.doLabel = 1 - - if entry.device.doLabel is not None: - entry.fsystem.labelDevice(entry, chroot) - - def formatEntry(self, entry, chroot): - if entry.mountpoint: - log.info("formatting %s as %s" %(entry.mountpoint, entry.fsystem.name)) - entry.fsystem.clobberDevice(entry, chroot) - entry.fsystem.formatDevice(entry, self.progressWindow, chroot) - - def getMigratableEntries(self): - retval = [] - for entry in self.entries: - if entry.origfsystem and entry.origfsystem.isMigratable(): - retval.append(entry) - - return retval - - def formattablePartitions(self): - list = [] - for entry in self.entries: - if entry.fsystem.isFormattable(): - list.append (entry) - return list - - def createLogicalVolumes (self, chroot='/'): - vgs = {} - # first set up the volume groups - for entry in self.entries: - if entry.fsystem.name == "volume group (LVM)": - entry.device.setupDevice(chroot) - vgs[entry.device.name] = entry.device - - # then set up the logical volumes - for entry in self.entries: - if isinstance(entry.device, LogicalVolumeDevice): - vg = None - if vgs.has_key(entry.device.vgname): - vg = vgs[entry.device.vgname] - entry.device.setupDevice(chroot, vgdevice = vg) - self.volumesCreated = 1 - - - def makeFilesystems (self, chroot='/', skiprootfs=False): - formatted = [] - notformatted = [] - for entry in self.entries: - if (not entry.fsystem.isFormattable() or not entry.getFormat() - or entry.isMounted()): - notformatted.append(entry) - continue - # FIXME: this is a bit of a hack, but works - if (skiprootfs and entry.mountpoint == '/'): - formatted.append(entry) - continue - try: - self.formatEntry(entry, chroot) - formatted.append(entry) - except SystemError: - if self.messageWindow: - self.messageWindow(_("Error"), - _("An error occurred trying to " - "format %s. This problem is " - "serious, and the install cannot " - "continue.\n\n" - "Press to exit the installer.") - % (entry.device.getDevice(),)) - sys.exit(0) - - for entry in formatted: - try: - self.labelEntry(entry, chroot) - except SystemError: - # should be OK, we'll still use the device name to mount. - pass - - # go through and have labels for the ones we don't format - for entry in notformatted: - dev = entry.device.getDevice() - if not dev or dev == "none": - continue - if not entry.mountpoint or entry.mountpoint == "swap": - continue - try: - label = isys.readFSLabel(dev) - except: - continue - - if label: - entry.setLabel(label) - elif entry.fsystem.isSupported(): - self.labelEntry(entry, chroot) - - def haveMigratedFilesystems(self): - return self.migratedfs - - def migrateFilesystems (self, anaconda): - if self.migratedfs: - return - - for entry in self.entries: - if not entry.origfsystem: - continue - - if not entry.origfsystem.isMigratable() or not entry.getMigrate(): - continue - try: - entry.origfsystem.migrateFileSystem(entry, self.messageWindow, - anaconda.rootPath) - except SystemError: - if self.messageWindow: - self.messageWindow(_("Error"), - _("An error occurred trying to " - "migrate %s. This problem is " - "serious, and the install cannot " - "continue.\n\n" - "Press to exit the installer.") - % (entry.device.getDevice(),)) - sys.exit(0) - - # we need to unmount and remount so that we're mounted as the - # new fstype as we want to use the new filesystem type during - # the upgrade for ext3->ext4 migrations - if self.isActive(): - self.umountFilesystems(anaconda.rootPath) - self.mountFilesystems(anaconda) - self.turnOnSwap(anaconda.rootPath) - - self.migratedfs = 1 - - def mountFilesystems(self, anaconda, raiseErrors = 0, readOnly = 0, skiprootfs = 0): - protected = anaconda.id.partitions.protectedPartitions() - - for entry in self.entries: - # Don't try to mount a protected partition, since it will already - # have been mounted as the installation source. - if protected and entry.device.getDevice() in protected and os.path.ismount("/mnt/isodir"): - continue - - if not entry.fsystem.isMountable() or (skiprootfs and entry.mountpoint == '/'): - continue - - try: - log.info("trying to mount %s on %s" %(entry.device.setupDevice(), entry.mountpoint,)) - entry.mount(anaconda.rootPath, readOnly = readOnly) - self.mountcount = self.mountcount + 1 - except OSError, (num, msg): - if self.messageWindow: - if num == errno.EEXIST: - self.messageWindow(_("Invalid mount point"), - _("An error occurred when trying " - "to create %s. Some element of " - "this path is not a directory. " - "This is a fatal error and the " - "install cannot continue.\n\n" - "Press 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 to exit the " - "installer.") % (entry.mountpoint, - msg)) - log.error("OSError: (%d) %s" % (num, msg) ) - sys.exit(0) - except SystemError, (num, msg): - if raiseErrors: - raise SystemError, (num, msg) - if self.messageWindow: - if not entry.fsystem.isLinuxNativeFS(): - ret = self.messageWindow(_("Unable to mount filesystem"), - _("An error occurred mounting " - "device %s as %s. You may " - "continue installation, but " - "there may be problems.") % - (entry.device.getDevice(), - entry.mountpoint), - type="custom", custom_icon="warning", - custom_buttons=[_("_Exit installer"), - _("_Continue")]) - - if ret == 0: - sys.exit(0) - else: - continue - else: - if anaconda.id.getUpgrade() and not (entry.getLabel() or entry.getUuid()) and entry.device.getDevice().startswith("/dev"): - errStr = _("Error mounting device %s as %s: " - "%s\n\n" - "Devices in /etc/fstab should be specified " - "by label or UUID, not by device name." - "\n\n" - "Press OK to exit the installer.") % (entry.device.getDevice(), entry.mountpoint, msg) - else: - errStr = _("Error mounting device %s as %s: " - "%s\n\n" - "Press OK to exit the installer.") % (entry.device.getDevice(), entry.mountpoint, msg) - - self.messageWindow(_("Error"), errStr) - - log.error("SystemError: (%d) %s" % (num, msg) ) - sys.exit(0) - - self.makeLVMNodes(anaconda.rootPath) - - def makeLVMNodes(self, instPath, trylvm1 = 0): - # XXX hack to make the device node exist for the root fs if - # it's a logical volume so that mkinitrd can create the initrd. - root = self.getEntryByMountPoint("/") - if not root: - if self.messageWindow: - self.messageWindow(_("Error"), - _("Error finding / entry.\n\n" - "This is most likely means that " - "your fstab is incorrect." - "\n\n" - "Press OK to exit the installer.")) - sys.exit(0) - - rootlvm1 = 0 - if trylvm1: - dev = root.device.getDevice() - # lvm1 major is 58 - if os.access("%s/dev/%s" %(instPath, dev), os.R_OK) and posix.major(os.stat("%s/dev/%s" %(instPath, dev)).st_rdev) == 58: - rootlvm1 = 1 - - if isinstance(root.device, LogicalVolumeDevice) or rootlvm1: - # now make sure all of the device nodes exist. *sigh* - rc = lvm.vgmknodes() - - rootDev = "/dev/%s" % (root.device.getDevice(),) - rootdir = instPath + os.path.dirname(rootDev) - if not os.path.isdir(rootdir): - os.makedirs(rootdir) - - if root.device.crypto is None: - dmdev = "/dev/mapper/" + root.device.getDevice().replace("-","--").replace("/", "-") - else: - dmdev = "/dev/" + root.device.getDevice() - - if os.path.exists(instPath + dmdev): - os.unlink(instPath + dmdev) - if not os.path.isdir(os.path.dirname(instPath + dmdev)): - os.makedirs(os.path.dirname(instPath + dmdev)) - iutil.copyDeviceNode(dmdev, instPath + dmdev) - - # unlink existing so that we dtrt on upgrades - if os.path.exists(instPath + rootDev) and not root.device.crypto: - os.unlink(instPath + rootDev) - if not os.path.isdir(rootdir): - os.makedirs(rootdir) - - if root.device.crypto is None: - os.symlink(dmdev, instPath + rootDev) - - if not os.path.isdir("%s/etc/lvm" %(instPath,)): - os.makedirs("%s/etc/lvm" %(instPath,)) - - def filesystemSpace(self, chroot='/'): - space = [] - for entry in self.entries: - if not entry.isMounted(): - continue - # we can't put swap files on swap partitions; that's nonsense - if entry.mountpoint == "swap": - continue - path = "%s/%s" % (chroot, entry.mountpoint) - try: - space.append((entry.mountpoint, isys.pathSpaceAvailable(path))) - except SystemError: - log.error("failed to get space available in filesystemSpace() for %s" %(entry.mountpoint,)) - - def spaceSort(a, b): - (m1, s1) = a - (m2, s2) = b - - if (s1 > s2): - return -1 - elif s1 < s2: - return 1 - - return 0 - - space.sort(spaceSort) - return space - - def hasDirtyFilesystems(self, mountpoint): - ret = [] - - for entry in self.entries: - # XXX - multifsify, virtualize isdirty per fstype - if entry.fsystem.getName() != "ext2": continue - if entry.getFormat(): continue - if isinstance(entry.device.getDevice(), BindMountDevice): continue - - try: - if isys.ext2IsDirty(entry.device.getDevice()): - log.info("%s is a dirty ext2 partition" % entry.device.getDevice()) - ret.append(entry.device.getDevice()) - except Exception, e: - log.error("got an exception checking %s for being dirty, hoping it's not" %(entry.device.getDevice(),)) - - return ret - - def umountFilesystems(self, instPath, ignoreErrors = 0, swapoff = True): - # Unmount things bind mounted into the instPath here because they're - # not tracked by self.entries. - if os.path.ismount("%s/dev" % instPath): - isys.umount("%s/dev" % instPath, removeDir=0) - - # take a slice so we don't modify self.entries - reverse = self.entries[:] - reverse.reverse() - - for entry in reverse: - if entry.mountpoint == "swap" and not swapoff: - continue - entry.umount(instPath) - entry.device.cleanupDevice(instPath) - -class FileSystemSetEntry: - def __init__ (self, device, mountpoint, - fsystem=None, options=None, - origfsystem=None, migrate=0, - order=-1, fsck=-1, format=0, - fsprofile=None): - if not fsystem: - fsystem = fileSystemTypeGet("ext2") - self.device = device - self.mountpoint = mountpoint - self.fsystem = fsystem - self.origfsystem = origfsystem - self.migrate = migrate - self.resizeTargetSize = None - self.resizeOrigSize = None - self.options = options - self.mountcount = 0 - self.label = None - if fsck == -1: - self.fsck = fsystem.isChecked() - else: - self.fsck = fsck - if order == -1: - if mountpoint == '/': - self.order = 1 - elif self.fsck: - self.order = 2 - else: - self.order = 0 - else: - self.order = order - if format and not fsystem.isFormattable(): - raise RuntimeError, ("file system type %s is not formattable, " - "but has been added to fsset with format " - "flag on" % fsystem.getName()) - self.format = format - self.fsprofile = fsprofile - - def mount(self, chroot='/', devPrefix='/dev', readOnly = 0): - device = self.device.setupDevice(chroot, devPrefix=devPrefix) - - self.fsystem.mount(device, "%s" % (self.mountpoint,), - readOnly = readOnly, - bindMount = isinstance(self.device, - BindMountDevice), - instroot = chroot) - - self.mountcount = self.mountcount + 1 - - def umount(self, chroot='/'): - if self.mountcount > 0: - try: - self.fsystem.umount(self.device, "%s/%s" % (chroot, - self.mountpoint)) - self.mountcount = self.mountcount - 1 - except RuntimeError: - pass - - def setFileSystemType(self, fstype): - self.fsystem = fstype - - def getMountPoint(self): - return self.mountpoint - - def getOptions(self): - options = self.options - if not options: - options = self.fsystem.getDefaultOptions(self.mountpoint) - return options + self.device.getDeviceOptions() - - def setFormat (self, state): - if self.migrate and state: - raise ValueError, "Trying to set format bit on when migrate is set!" - self.format = state - - def getFormat (self): - return self.format - - def setMigrate (self, state): - if self.format and state: - raise ValueError, "Trying to set migrate bit on when format is set!" - - self.migrate = state - - def getMigrate (self): - return self.migrate - - def setResizeTarget (self, targetsize, size): - if not self.fsystem.isResizable() and targetsize is not None: - raise ValueError, "Can't set a resize target for a non-resizable filesystem" - self.resizeTargetSize = targetsize - self.resizeOrigSize = size - - def getResizeTarget (self): - return self.resizeTargetSize - - def isMounted (self): - return self.mountcount > 0 - - def getLabel (self): - return self.label - - def getUuid (self): - return isys.readFSUuid(self.device.getDevice()) - - def setLabel (self, label): - self.label = label - - def __str__(self): - if not self.mountpoint: - mntpt = "None" - else: - mntpt = self.mountpoint - - str = ("fsentry -- device: %(device)s mountpoint: %(mountpoint)s\n" - " fsystem: %(fsystem)s format: %(format)s\n" - " ismounted: %(mounted)s options: '%(options)s'\n" - " label: %(label)s fsprofile: %(fsprofile)s\n"% - {"device": self.device.getDevice(), "mountpoint": mntpt, - "fsystem": self.fsystem.getName(), "format": self.format, - "mounted": self.mountcount, "options": self.getOptions(), - "label": self.label, "fsprofile": self.fsprofile}) - return str - - -class Device: - def __init__(self, device = "none", encryption=None): - self.device = device - self.label = None - self.isSetup = 0 - self.doLabel = 1 - self.deviceOptions = "" - if encryption: - self.crypto = encryption - # mount by device since the name is based only on UUID - self.doLabel = None - if device not in ("none", None): - self.crypto.setDevice(device) - else: - self.crypto = None - - def getComment (self): - return "" - - def getDevice (self, asBoot = 0): - if self.crypto: - return self.crypto.getDevice() - else: - return self.device - - def setupDevice (self, chroot='/', devPrefix='/dev/'): - return self.device - - def cleanupDevice (self, chroot, devPrefix='/dev/'): - if self.crypto: - self.crypto.closeDevice() - - def solidify (self): - pass - - def getName(self): - return self.__class__.__name__ - - def getLabel(self): - try: - return isys.readFSLabel(self.setupDevice()) - except: - return "" - - def setAsNetdev(self): - """Ensure we're set up so that _netdev is in our device options.""" - if "_netdev" not in self.deviceOptions: - self.deviceOptions += ",_netdev" - - def isNetdev(self): - """Check to see if we're set as a netdev""" - if "_netdev" in self.deviceOptions: - return True - return False - - def getDeviceOptions(self): - return self.deviceOptions - -class DevDevice(Device): - """Device with a device node rooted in /dev that we just always use - the pre-created device node for.""" - def __init__(self, dev): - Device.__init__(self, device=dev) - - def getDevice(self, asBoot = 0): - return self.device - - def setupDevice(self, chroot='/', devPrefix='/dev'): - #We use precreated device but we have to make sure that the device exists - path = '/dev/%s' % (self.getDevice(),) - return path - -class RAIDDevice(Device): - # XXX usedMajors does not take in account any EXISTING md device - # on the system for installs. We need to examine all partitions - # to investigate which minors are really available. - usedMajors = {} - - # members is a list of Device based instances that will be - # a part of this raid device - def __init__(self, level, members, minor=-1, spares=0, existing=0, - chunksize = 64, encryption=None): - Device.__init__(self, encryption=encryption) - self.level = level - self.members = members - self.spares = spares - self.numDisks = len(members) - spares - self.isSetup = existing - self.doLabel = None - if chunksize is not None: - self.chunksize = chunksize - else: - self.chunksize = 256 - - if len(members) < spares: - raise RuntimeError, ("you requested more spare devices " - "than online devices!") - - if level == 5: - if self.numDisks < 3: - raise RuntimeError, "RAID 5 requires at least 3 online members" - - # there are 32 major md devices, 0...31 - if minor == -1 or minor is None: - for I in range(32): - if not RAIDDevice.usedMajors.has_key(I): - minor = I - break - - if minor == -1: - raise RuntimeError, ("Unable to allocate minor number for " - "raid device") - - RAIDDevice.usedMajors[minor] = None - self.device = "md" + str(minor) - self.minor = minor - - if self.crypto: - self.crypto.setDevice(self.device) - - # make sure the list of raid members is sorted - self.members.sort(cmp=lambda x,y: cmp(x.getDevice(),y.getDevice())) - - def __del__ (self): - del RAIDDevice.usedMajors[self.minor] - - def ext2Args (self): - if self.level == 5: - return [ '-R', 'stride=%d' % ((self.numDisks - 1) * 16) ] - elif self.level == 0: - return [ '-R', 'stride=%d' % (self.numDisks * 16) ] - return [] - - def mdadmLine (self, devPrefix="/dev"): - levels = { 0: "raid0", - 1: "raid1", - 4: "raid5", - 5: "raid5", - 6: "raid6", - 10: "raid10" } - - # If we can't find the device for some reason, revert to old behavior. - try: - (dev, devices, level, numActive) = raid.lookup_raid_device (self.device) - except KeyError: - devices = [] - - # First loop over all the devices that make up the RAID trying to read - # the superblock off each. If we read a superblock, return a line that - # can go into the mdadm.conf. If we fail, fall back to the old method - # of using the super-minor. - for d in devices: - try: - (major, minor, uuid, level, nrDisks, totalDisks, mdMinor) = \ - isys.raidsb(d) - return "ARRAY %s/%s level=%s num-devices=%d uuid=%s\n" \ - %(devPrefix, self.device, levels[level], nrDisks, uuid) - except ValueError: - pass - - return "ARRAY %s/%s super-minor=%s\n" %(devPrefix, self.device, - self.minor) - - def raidTab (self, devPrefix='/dev'): - entry = "" - entry = entry + "raiddev %s/%s\n" % (devPrefix, - self.device,) - entry = entry + "raid-level %d\n" % (self.level,) - entry = entry + "nr-raid-disks %d\n" % (self.numDisks,) - entry = entry + "chunk-size %s\n" %(self.chunksize,) - entry = entry + "persistent-superblock 1\n" - entry = entry + "nr-spare-disks %d\n" % (self.spares,) - i = 0 - for device in [m.getDevice() for m in self.members[:self.numDisks]]: - entry = entry + " device %s/%s\n" % (devPrefix, - device) - entry = entry + " raid-disk %d\n" % (i,) - i = i + 1 - i = 0 - for device in [m.getDevice() for m in self.members[self.numDisks:]]: - entry = entry + " device %s/%s\n" % (devPrefix, - device) - entry = entry + " spare-disk %d\n" % (i,) - i = i + 1 - return entry - - def setupDevice (self, chroot="/", devPrefix='/dev'): - if not self.isSetup: - memberDevs = [] - for pd in self.members: - memberDevs.append(pd.setupDevice(chroot, devPrefix=devPrefix)) - if pd.isNetdev(): self.setAsNetdev() - - args = ["--create", "/dev/%s" %(self.device,), - "--run", "--chunk=%s" %(self.chunksize,), - "--level=%s" %(self.level,), - "--raid-devices=%s" %(self.numDisks,)] - - if self.spares > 0: - args.append("--spare-devices=%s" %(self.spares,),) - - args.extend(memberDevs) - log.info("going to run: %s" %(["mdadm"] + args,)) - iutil.execWithRedirect ("mdadm", args, - stderr="/dev/tty5", stdout="/dev/tty5", - searchPath = 1) - raid.register_raid_device(self.device, - [m.getDevice() for m in self.members], - self.level, self.numDisks) - self.isSetup = 1 - else: - isys.raidstart(self.device, self.members[0].getDevice()) - - if self.crypto: - self.crypto.formatDevice() - self.crypto.openDevice() - node = "%s/%s" % (devPrefix, self.crypto.getDevice()) - else: - node = "%s/%s" % (devPrefix, self.device) - - return node - - def getDevice (self, asBoot = 0): - if not asBoot and self.crypto: - return self.crypto.getDevice() - elif not asBoot: - return self.device - else: - return self.members[0].getDevice(asBoot=asBoot) - - def solidify(self): - return - -ext2 = fileSystemTypeGet("ext2") -ext2.registerDeviceArgumentFunction(RAIDDevice, RAIDDevice.ext2Args) - -class VolumeGroupDevice(Device): - def __init__(self, name, physvols, pesize = 32768, existing = 0): - """Creates a VolumeGroupDevice. - - name is the name of the volume group - physvols is a list of Device objects which are the physical volumes - pesize is the size of physical extents in kilobytes - existing is whether this vg previously existed. - """ - - Device.__init__(self) - self.physicalVolumes = physvols - self.isSetup = existing - self.name = name - self.device = name - self.isSetup = existing - - self.physicalextentsize = pesize - - def setupDevice (self, chroot="/", devPrefix='/dev/'): - nodes = [] - for volume in self.physicalVolumes: - # XXX the lvm tools are broken and will only work for /dev - node = volume.setupDevice(chroot, devPrefix="/dev") - if volume.isNetdev(): self.setAsNetdev() - - # XXX I should check if the pv is set up somehow so that we - # can have preexisting vgs and add new pvs to them. - if not self.isSetup: - lvm.pvcreate(node) - nodes.append(node) - - if not self.isSetup: - lvm.vgcreate(self.name, self.physicalextentsize, nodes) - self.isSetup = 1 - else: - lvm.vgscan() - lvm.vgactivate() - - return "/dev/%s" % (self.name,) - - def solidify(self): - return - -class LogicalVolumeDevice(Device): - # note that size is in megabytes! - def __init__(self, vgname, size, lvname, vg, existing = 0, encryption=None): - Device.__init__(self, encryption=encryption) - self.vgname = vgname - self.size = size - self.name = lvname - self.isSetup = 0 - self.isSetup = existing - self.doLabel = None - self.vg = vg - - # these are attributes we might want to expose. or maybe not. - # self.chunksize - # self.stripes - # self.stripesize - # self.extents - # self.readaheadsectors - - def setupDevice(self, chroot="/", devPrefix='/dev', vgdevice = None): - if self.crypto: - self.crypto.setDevice("mapper/%s-%s" % (self.vgname, self.name)) - - if not self.isSetup: - lvm.lvcreate(self.name, self.vgname, self.size) - self.isSetup = 1 - - if vgdevice and vgdevice.isNetdev(): - self.setAsNetdev() - - if self.crypto: - self.crypto.formatDevice() - self.crypto.openDevice() - - return "/dev/%s" % (self.getDevice(),) - - def getDevice(self, asBoot = 0): - if self.crypto and not asBoot: - device = self.crypto.getDevice() - else: - device = "%s/%s" % (self.vgname, self.name) - - return device - - def solidify(self): - return - - -class PartitionDevice(Device): - def __init__(self, partition, encryption=None): - if type(partition) != types.StringType: - raise ValueError, "partition must be a string" - Device.__init__(self, device=partition, encryption=encryption) - - (disk, pnum) = getDiskPart(partition) - if isys.driveIsIscsi(disk): - self.setAsNetdev() - - def getDevice(self, asBoot = 0): - if self.crypto and not asBoot: - return self.crypto.getDevice() - else: - return self.device - - def setupDevice(self, chroot="/", devPrefix='/dev'): - path = '%s/%s' % (devPrefix, self.device) - if self.crypto: - self.crypto.formatDevice() - self.crypto.openDevice() - path = "%s/%s" % (devPrefix, self.crypto.getDevice()) - return path - -class PartedPartitionDevice(PartitionDevice): - def __init__(self, partition): - Device.__init__(self) - self.device = None - self.partition = partition - - def getDevice(self, asBoot = 0): - if not self.partition: - return self.device - - return self.partition.getDeviceNodeName() - - def solidify(self): - # drop reference on the parted partition object and note - # the current minor number allocation - self.device = self.getDevice() - self.partition = None - -class BindMountDevice(Device): - def __init__(self, directory): - Device.__init__(self) - self.device = directory - - def setupDevice(self, chroot="/", devPrefix="/tmp"): - return chroot + self.device - -class SwapFileDevice(Device): - def __init__(self, file): - Device.__init__(self) - self.device = file - self.size = 0 - - def setSize (self, size): - self.size = size - - def setupDevice (self, chroot="/", devPrefix='/dev'): - file = os.path.normpath(chroot + self.getDevice()) - if not os.access(file, os.R_OK): - if self.size: - # make sure the permissions are set properly - fd = os.open(file, os.O_CREAT, 0600) - os.close(fd) - isys.ddfile(file, self.size, None) - else: - raise SystemError, (0, "swap file creation necessary, but " - "required size is unknown.") - return file - -# This is a device that describes a swap file that is sitting on -# the loopback filesystem host for partitionless installs. -# The piggypath is the place where the loopback file host filesystem -# will be mounted -class PiggybackSwapFileDevice(SwapFileDevice): - def __init__(self, piggypath, file): - SwapFileDevice.__init__(self, file) - self.piggypath = piggypath - - def setupDevice(self, chroot="/", devPrefix='/dev'): - return SwapFileDevice.setupDevice(self, self.piggypath, devPrefix) - -class LoopbackDevice(Device): - def __init__(self, hostPartition, hostFs): - Device.__init__(self) - self.host = "/dev/" + hostPartition - self.hostfs = hostFs - self.device = "loop1" - - def setupDevice(self, chroot="/", devPrefix='/dev/'): - if not self.isSetup: - isys.mount(self.host[5:], "/mnt/loophost", fstype = "vfat") - self.device = allocateLoopback("/mnt/loophost/redhat.img") - if not self.device: - raise SystemError, "Unable to allocate loopback device" - self.isSetup = 1 - path = '%s/%s' % (devPrefix, self.getDevice()) - else: - path = '%s/%s' % (devPrefix, self.getDevice()) - path = os.path.normpath(path) - return path - - def getComment (self): - return "# LOOP1: %s %s /redhat.img\n" % (self.host, self.hostfs) - -def makeDevice(dev): - cryptoDev = partitions.lookup_cryptodev(dev) - if cryptoDev and cryptoDev.getDevice() == dev: - dev = cryptoDev.getDevice(encrypted=True) - - if dev.startswith('md'): - try: - (mdname, devices, level, numActive) = raid.lookup_raid_device(dev) - # convert devices to Device instances and sort out encryption - devList = [] - for dev in devices: - cryptoMem = partitions.lookup_cryptodev(dev) - if cryptoMem and cryptoMem.getDevice() == dev: - dev = cryptoMem.getDevice(encrypted=True) - - devList.append(PartitionDevice(dev, encryption=cryptoMem)) - - device = RAIDDevice(level, devList, - minor=int(mdname[2:]), - spares=len(devices) - numActive, - existing=1, encryption=cryptoDev) - except KeyError: - device = PartitionDevice(dev, encryption=cryptoDev) - else: - device = PartitionDevice(dev, encryption=cryptoDev) - return device - -def findBackingDevInCrypttab(mappingName): - backingDev = None - try: - lines = open("/mnt/sysimage/etc/crypttab").readlines() - except IOError, e: - pass - else: - for line in lines: - fields = line.split() - if len(fields) < 2: - continue - if fields[0] == mappingName: - backingDev = fields[1] - break - - return backingDev - -# XXX fix RAID -def readFstab (anaconda): - def createMapping(dict): - mapping = {} - dupes = [] - - for device, info in dict.items(): - if not mapping.has_key(info): - mapping[info] = device - elif not info in dupes: - dupes.append(info) - - return (mapping, dupes) - - def showError(label, intf): - if intf: - intf.messageWindow(_("Duplicate Labels"), - _("Multiple devices on your system are " - "labelled %s. Labels across devices must be " - "unique for your system to function " - "properly.\n\n" - "Please fix this problem and restart the " - "installation process.") %(label,), - type="custom", custom_icon="error", - custom_buttons=[_("_Exit installer")]) - sys.exit(0) - else: - log.warning("Duplicate labels for %s, but no intf so trying " - "to continue" %(label,)) - - path = anaconda.rootPath + '/etc/fstab' - intf = anaconda.intf - fsset = FileSystemSet() - - # first, we look at all the disks on the systems and get any ext2/3 - # labels off of the filesystem. - # temporary, to get the labels - diskset = partedUtils.DiskSet(anaconda) - diskset.openDevices() - labels = diskset.getInfo() - uuids = diskset.getInfo(readFn=lambda d: isys.readFSUuid(d)) - - (labelToDevice, labelDupes) = createMapping(labels) - (uuidToDevice, uuidDupes) = createMapping(uuids) - - loopIndex = {} - - f = open (path, "r") - lines = f.readlines () - f.close() - - for line in lines: - fields = string.split (line) - - if not fields: continue - - if line[0] == "#": - # skip all comments - continue - - # all valid fstab entries have 6 fields; if the last two are missing - # they are assumed to be zero per fstab(5) - if len(fields) < 4: - continue - elif len(fields) == 4: - fields.append(0) - fields.append(0) - elif len(fields) == 5: - fields.append(0) - elif len(fields) > 6: - continue - if string.find(fields[3], "noauto") != -1: continue - - # shenanigans to handle ext3,ext2 format in fstab - fstotry = fields[2] - if fstotry.find(","): - fstotry = fstotry.split(",") - else: - fstotry = [ fstotry ] - - # ext4 used to be listed as ext4dev, so handle that possibility - for i in range(0, len(fstotry)): - if fstotry[i] == "ext4dev": - fstotry[i] = "ext4" - - fsystem = None - for fs in fstotry: - # if we don't support mounting the filesystem, continue - if not fileSystemTypes.has_key(fs): - continue - fsystem = fileSystemTypeGet(fs) - break - # "none" is valid as an fs type for bind mounts (#151458) - if fsystem is None and (string.find(fields[3], "bind") == -1): - continue - - label = None - if fields[0] == "none": - device = Device() - elif ((string.find(fields[3], "bind") != -1) and - fields[0].startswith("/")): - # it's a bind mount, they're Weird (tm) - device = BindMountDevice(fields[0]) - fsystem = fileSystemTypeGet("bind") - elif len(fields) >= 6 and fields[0].startswith('LABEL='): - label = fields[0][6:] - if label in labelDupes: - showError(label, intf) - - if labelToDevice.has_key(label): - device = makeDevice(labelToDevice[label]) - else: - log.warning ("fstab file has LABEL=%s, but this label " - "could not be found on any file system", label) - # bad luck, skip this entry. - continue - elif len(fields) >= 6 and fields[0].startswith('UUID='): - uuid = fields[0][5:] - if uuid in uuidDupes: - showError(uuid, intf) - - if uuidToDevice.has_key(uuid): - device = makeDevice(uuidToDevice[uuid]) - else: - log.warning ("fstab file has UUID=%s, but this UUID" - "could not be found on any file system", uuid) - # bad luck, skip this entry. - continue - elif fields[2] == "swap" and not fields[0].startswith('/dev/'): - # swap files - file = fields[0] - - if file.startswith('/initrd/loopfs/'): - file = file[14:] - device = PiggybackSwapFileDevice("/mnt/loophost", file) - else: - device = SwapFileDevice(file) - elif fields[0].startswith('/dev/loop'): - # look up this loop device in the index to find the - # partition that houses the filesystem image - # XXX currently we assume /dev/loop1 - if loopIndex.has_key(device): - (dev, fs) = loopIndex[device] - device = LoopbackDevice(dev, fs) - elif fields[0].startswith("/dev/mapper/luks-"): - backingDev = findBackingDevInCrypttab(fields[0][12:]) - log.debug("device %s has backing device %s" % (fields[0], - backingDev)) - if backingDev is None: - log.error("unable to resolve backing device for %s" % fields[0]) - continue - elif backingDev.startswith('LABEL='): - label = backingDev[6:] - if label in labelDupes: - showError(label, intf) - - if labelToDevice.has_key(label): - device = makeDevice(labelToDevice[label]) - else: - log.warning ("crypttab file has LABEL=%s, but this label " - "could not be found on any file system", label) - # bad luck, skip this entry. - continue - elif backingDev.startswith('UUID='): - uuid = backingDev[5:] - if uuid in uuidDupes: - showError(uuid, intf) - - if uuidToDevice.has_key(uuid): - device = makeDevice(uuidToDevice[uuid]) - else: - log.warning ("crypttab file has UUID=%s, but this UUID" - "could not be found on any file system", uuid) - # bad luck, skip this entry. - continue - else: - device = makeDevice(backingDev[5:]) - elif fields[0].startswith('/dev/'): - # Older installs may have lines starting with things like /dev/proc - # so watch out for that on upgrade. - if fsystem is not None and isinstance(fsystem, PseudoFileSystem): - device = Device(device = fields[0][5:]) - else: - device = makeDevice(fields[0][5:]) - else: - device = Device(device = fields[0]) - - # if they have a filesystem being mounted as auto, we need - # to sniff around a bit to figure out what it might be - # if we fail at all, though, just ignore it - if fsystem == "auto" and device.getDevice() != "none": - try: - tmp = isys.readFSType("/dev/%s" %(device.setupDevice(),)) - if tmp is not None: - fsystem = tmp - except: - pass - - entry = FileSystemSetEntry(device, fields[1], fsystem, fields[3], - origfsystem=fsystem) - if label: - entry.setLabel(label) - fsset.add(entry) - return fsset - -def allocateLoopback(file): - found = 1 - for i in range(8): - path = "/dev/loop%d" % (i,) - try: - isys.losetup(path, file) - found = 1 - except SystemError: - continue - break - if found: - return path - return None - -def ext2FormatFilesystem(argList, messageFile, windowCreator, mntpoint): - if windowCreator: - w = windowCreator(_("Formatting"), - _("Formatting %s file system...") % (mntpoint,), 100) - else: - w = None - - fd = os.open(messageFile, os.O_RDWR | os.O_CREAT | os.O_APPEND) - p = os.pipe() - childpid = os.fork() - if not childpid: - os.close(p[0]) - os.dup2(p[1], 1) - os.dup2(fd, 2) - os.close(p[1]) - os.close(fd) - - env = os.environ - configs = [ "/tmp/updates/mke2fs.conf", - "/etc/mke2fs.conf", - ] - for config in configs: - if os.access(config, os.R_OK): - env['MKE2FS_CONFIG'] = config - break - - os.execvpe(argList[0], argList, env) - log.critical("failed to exec %s", argList) - os._exit(1) - - os.close(p[1]) - - # ignoring SIGCHLD would be cleaner then ignoring EINTR, but - # we can't use signal() in this thread? - - s = 'a' - while s and s != '\b': - try: - s = os.read(p[0], 1) - except OSError, args: - (num, str) = args - if (num != 4): - raise IOError, args - - os.write(fd, s) - - num = '' - while s: - try: - s = os.read(p[0], 1) - os.write(fd, s) - - if s != '\b': - try: - num = num + s - except: - pass - else: - if num and len(num): - l = string.split(num, '/') - try: - val = (int(l[0]) * 100) / int(l[1]) - except (IndexError, TypeError): - pass - else: - w and w.set(val) - num = '' - except OSError, args: - (errno, str) = args - if (errno != 4): - raise IOError, args - - try: - (pid, status) = os.waitpid(childpid, 0) - except OSError, (num, msg): - log.critical("exception from waitpid while formatting: %s %s" %(num, msg)) - status = None - os.close(fd) - - w and w.pop() - - # *shrug* no clue why this would happen, but hope that things are fine - if status is None: - return 0 - - if os.WIFEXITED(status) and (os.WEXITSTATUS(status) == 0): - return 0 - - return 1 - -# copy and paste job from booty/bootloaderInfo.py... -def getDiskPart(dev): - cut = len(dev) - if (dev.startswith('rd/') or dev.startswith('ida/') or - dev.startswith('cciss/') or dev.startswith('sx8/') or - dev.startswith('mapper/') or dev.startswith('mmcblk')): - if dev[-2] == 'p': - cut = -1 - elif dev[-3] == 'p': - cut = -2 - else: - if dev[-2] in string.digits: - cut = -2 - elif dev[-1] in string.digits: - cut = -1 - - name = dev[:cut] - - # hack off the trailing 'p' from /dev/cciss/*, for example - if name[-1] == 'p': - for letter in name: - if letter not in string.letters and letter != "/": - name = name[:-1] - break - - if cut < 0: - partNum = int(dev[cut:]) - 1 - else: - partNum = None - - return (name, partNum) diff --git a/iscsi.py b/iscsi.py deleted file mode 100644 index 214a9c877..000000000 --- a/iscsi.py +++ /dev/null @@ -1,332 +0,0 @@ -# -# iscsi.py - iscsi class -# -# Copyright (C) 2005, 2006 IBM, Inc. All rights reserved. -# Copyright (C) 2006 Red Hat, Inc. All rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# - -from constants import * -import os -import errno -import string -import signal -import iutil -import isys -from flags import flags -import logging -import shutil -import time -import md5, random -import partedUtils -log = logging.getLogger("anaconda") - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -has_libiscsi = True -try: - import libiscsi -except: - has_libiscsi = False - -# Note that stage2 copies all files under /sbin to /usr/sbin -global ISCSID -ISCSID="" -global ISCSIADM -ISCSIADM = "" -INITIATOR_FILE="/etc/iscsi/initiatorname.iscsi" - -def find_iscsi_files(): - global ISCSID - if ISCSID == "": - for dir in ("/usr/sbin", "/tmp/updates", "/mnt/source/RHupdates"): - path="%s/iscsid" % (dir,) - if os.access(path, os.X_OK): - ISCSID=path - global ISCSIADM - if ISCSIADM == "": - for dir in ("/usr/sbin", "/tmp/updates", "/mnt/source/RHupdates"): - path="%s/iscsiadm" % (dir,) - if os.access(path, os.X_OK): - ISCSIADM=path - -def has_iscsi(): - find_iscsi_files() - if ISCSID == "" or ISCSIADM == "" or not has_libiscsi: - return False - - log.info("ISCSID is %s" % (ISCSID,)) - log.info("ISCSIADM is %s" % (ISCSIADM,)) - - # make sure the module is loaded - if not os.access("/sys/module/iscsi_tcp", os.X_OK): - return False - return True - -def iscsi_get_node_record(node_settings, record): - for line in node_settings: - if line.startswith(record): - words = line.split(" = ") - if len(words) == 2: - return words[1] - # should never happen but better safe then sorry - break - - return None - -# FIXME replace with libiscsi use -def iscsi_make_node_autostart(disk): - sysfs_path = os.path.realpath("/sys/block/%s/device" %(disk,)) - argv = [ "-m", "session", "-r", sysfs_path ] - log.debug("iscsiadm %s" %(string.join(argv),)) - node_settings = iutil.execWithCapture(ISCSIADM, argv, stderr="/dev/tty5").splitlines() - node_name = iscsi_get_node_record(node_settings, "node.name") - argv = [ "-m", "node", "-T", node_name, "-o", "update", "-n", - "node.startup", "-v", "automatic" ] - log.debug("iscsiadm %s" %(string.join(argv),)) - iutil.execWithRedirect(ISCSIADM, argv, - stdout = "/dev/tty5", stderr="/dev/tty5") - -def randomIname(): - """Generate a random initiator name the same way as iscsi-iname""" - - s = "iqn.1994-05.com.fedora:01." - m = md5.md5() - u = os.uname() - for i in u: - m.update(i) - dig = m.hexdigest() - - for i in range(0, 6): - s += dig[random.randrange(0, 32)] - return s - -def stabilize(intf = None): - # Wait for udev to create the devices for the just added disks - if intf: - w = intf.waitWindow(_("Scanning iSCSI nodes"), - _("Scanning iSCSI nodes")) - # It is possible when we get here the events for the new devices - # are not send yet, so sleep to make sure the events are fired - time.sleep(2) - iutil.execWithRedirect("udevadm", [ "settle" ], - stdout = "/dev/tty5", stderr="/dev/tty5", - searchPath = 1) - if intf: - w.pop() - -class iscsi(object): - def __init__(self): - self.nodes = [] - self._initiator = "" - self.initiatorSet = False - self.started = False - - try: - initiatorname = libiscsi.get_firmware_initiator_name() - self._initiator = initiatorname - self.initiatorSet = True - except: - pass - - def _getInitiator(self): - if self._initiator != "": - return self._initiator - - return randomIname() - - def _setInitiator(self, val): - if self._initiator != "" and val != self._initiator: - raise ValueError, "Unable to change iSCSI initiator name once set" - if len(val) == 0: - raise ValueError, "Must provide a non-zero length string" - self._initiator = val - self.initiatorSet = True - - initiator = property(_getInitiator, _setInitiator) - - def _startIBFT(self, intf = None): - if not flags.ibft: - return - - try: - found_nodes = libiscsi.discover_firmware() - except: - # an exception here means there is no ibft firmware, just return - return - - for node in found_nodes: - try: - node.login() - self.nodes.append(node) - except: - # FIXME, what to do when we cannot log in to a firmware - # provided node ?? - pass - - stabilize(intf) - - def startup(self, intf = None): - if self.started: - return - - if not has_iscsi(): - return - - if not self.initiatorSet: - log.info("no initiator set") - return - - if intf: - w = intf.waitWindow(_("Initializing iSCSI initiator"), - _("Initializing iSCSI initiator")) - - log.debug("Setting up %s" % (INITIATOR_FILE, )) - log.info("iSCSI initiator name %s", self.initiator) - if os.path.exists(INITIATOR_FILE): - os.unlink(INITIATOR_FILE) - if not os.path.isdir("/etc/iscsi"): - os.makedirs("/etc/iscsi", 0755) - fd = os.open(INITIATOR_FILE, os.O_RDWR | os.O_CREAT) - os.write(fd, "InitiatorName=%s\n" %(self.initiator)) - os.close(fd) - - for dir in ['ifaces','isns','nodes','send_targets','slp','static']: - fulldir = "/var/lib/iscsi/%s" % (dir,) - if not os.path.isdir(fulldir): - os.makedirs(fulldir, 0755) - - log.info("iSCSI startup") - iutil.execWithRedirect(ISCSID, [], - stdout="/dev/tty5", stderr="/dev/tty5") - time.sleep(1) - - if intf: - w.pop() - - self._startIBFT(intf) - self.started = True - - def addTarget(self, ipaddr, port="3260", user=None, pw=None, - user_in=None, pw_in=None, intf=None): - authinfo = None - found = 0 - logged_in = 0 - - if not has_iscsi(): - raise IOError, _("iSCSI not available") - if not self.initiatorSet: - raise ValueError, _("No initiator name set") - - self.startup(intf) - - if user: - # Note may raise a ValueError - authinfo = libiscsi.chapAuthInfo(username=user, password=pw, - reverse_username=user_in, - reverse_password=pw_in) - # Note may raise an IOError - found_nodes = libiscsi.discover_sendtargets(address=ipaddr, - port=int(port), - authinfo=authinfo) - if found_nodes == None: - raise IOError, _("No iSCSI nodes discovered") - - if intf: - w = intf.waitWindow(_("Logging in to iSCSI nodes"), - _("Logging in to iSCSI nodes")) - - for node in found_nodes: - # skip nodes we already have - if node in self.nodes: - continue - - found = found + 1 - try: - if (authinfo): - node.setAuth(authinfo) - node.login() - self.nodes.append(node) - logged_in = logged_in + 1 - except: - # some nodes may require different credentials - pass - - if intf: - w.pop() - - if found == 0: - raise IOError, _("No new iSCSI nodes discovered") - - if logged_in == 0: - raise IOError, _("Could not log in to any of the discovered nodes") - - stabilize(intf) - - def writeKS(self, f): - if not self.initiatorSet: - return - f.write("iscsiname %s\n" %(self.initiator,)) - for n in self.nodes: - f.write("iscsi --ipaddr %s --port %s" %(n.address, n.port)) - auth = n.getAuth() - if auth: - f.write(" --user %s" %(n.username,)) - f.write(" --password %s" %(n.password,)) - if len(auth.reverse_username): - f.write(" --reverse-user %s" % (n.reverse_username,)) - f.write(" --reverse-password %s" % (n.reverse_password,)) - f.write("\n") - - def write(self, instPath, anaconda): - if not self.initiatorSet: - return - - if not flags.test: - root_drives = [ ] - req = anaconda.id.partitions.getRequestByMountPoint("/") - root_requests = anaconda.id.partitions.getUnderlyingRequests(req) - for req in root_requests: - for drive in req.drive: - part = anaconda.id.diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device) - if part: - break - if not part: - continue - if drive not in root_drives: - root_drives.append(drive) - - log.debug("iscsi.write: root_drives: %s" % (string.join(root_drives),)) - - # set iscsi nodes not used for root to autostart - for disk in anaconda.id.diskset.disks.keys(): - if isys.driveIsIscsi(disk) and not disk in root_drives: - iscsi_make_node_autostart(disk) - - if not os.path.isdir(instPath + "/etc/iscsi"): - os.makedirs(instPath + "/etc/iscsi", 0755) - fd = os.open(instPath + INITIATOR_FILE, os.O_RDWR | os.O_CREAT) - os.write(fd, "InitiatorName=%s\n" %(self.initiator)) - os.close(fd) - - # copy "db" files. *sigh* - if os.path.isdir(instPath + "/var/lib/iscsi"): - shutil.rmtree(instPath + "/var/lib/iscsi") - if os.path.isdir("/var/lib/iscsi"): - shutil.copytree("/var/lib/iscsi", instPath + "/var/lib/iscsi", - symlinks=True) - -# vim:tw=78:ts=4:et:sw=4 diff --git a/lvm.py b/lvm.py deleted file mode 100644 index 520619f82..000000000 --- a/lvm.py +++ /dev/null @@ -1,614 +0,0 @@ -# -# lvm.py - lvm probing control -# -# Copyright (C) 2002 Red Hat, Inc. All rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Author(s): Jeremy Katz -# - -import iutil -import os,sys -import string -import math -import isys -import re - -from flags import flags - -import logging -log = logging.getLogger("anaconda") - -from constants import * - -MAX_LV_SLOTS=256 - -lvmDevicePresent = 0 - -from errors import * - -def has_lvm(): - global lvmDevicePresent - - if not (os.access("/usr/sbin/lvm", os.X_OK) or - os.access("/sbin/lvm", os.X_OK)): - return - - f = open("/proc/devices", "r") - lines = f.readlines() - f.close() - - for line in lines: - try: - (dev, name) = line[:-1].split(' ', 2) - except: - continue - if name == "device-mapper": - lvmDevicePresent = 1 - break - return lvmDevicePresent -# now check to see if lvm is available -has_lvm() - -def lvmExec(*args): - try: - return iutil.execWithRedirect("lvm", args, stdout = lvmErrorOutput, - stderr = lvmErrorOutput, searchPath = 1) - except Exception, e: - log.error("error running lvm command: %s" %(e,)) - raise LvmError, args[0] - -def lvmCapture(*args): - try: - lvmout = iutil.execWithCapture("lvm", args, stderr = lvmErrorOutput) - lines = [] - for line in lvmout.split("\n"): - lines.append(line.strip().split(':')) - return lines - except Exception, e: - log.error("error running lvm command: %s" %(e,)) - raise LvmError, args[0] - -def vgscan(): - """Runs vgscan.""" - global lvmDevicePresent - - if flags.test or lvmDevicePresent == 0: - return - - rc = lvmExec("vgscan", "-v") - if rc: - log.error("running vgscan failed: %s" %(rc,)) -# lvmDevicePresent = 0 - -def vgmknodes(volgroup=None): - # now make the device nodes - args = ["vgmknodes", "-v"] - if volgroup: - args.append(volgroup) - rc = lvmExec(*args) - if rc: - log.error("running vgmknodes failed: %s" %(rc,)) -# lvmDevicePresent = 0 - -def vgcheckactive(volgroup = None): - """Check if volume groups are active - - volgroup - optional parameter to inquire about a specific volume group. - """ - global lvmDevicePresent - if flags.test or lvmDevicePresent == 0: - return False - - args = ["lvs", "--noheadings", "--units", "b", "--nosuffix", - "--separator", ":", "--options", "vg_name,lv_name,attr"] - for line in lvmCapture(*args): - try: - (vg, lv, attr) = line - except: - continue - - log.info("lv %s/%s, attr is %s" %(vg, lv, attr)) - if attr.find("a") == -1: - continue - - if volgroup is None or volgroup == vg: - return True - - return False - -def vgactivate(volgroup = None): - """Activate volume groups by running vgchange -ay. - - volgroup - optional single volume group to activate - """ - global lvmDevicePresent - if flags.test or lvmDevicePresent == 0: - return - - args = ["vgchange", "-ay", "-v"] - if volgroup: - args.append(volgroup) - rc = lvmExec(*args) - if rc: - log.error("running vgchange failed: %s" %(rc,)) -# lvmDevicePresent = 0 - vgmknodes(volgroup) - -def vgdeactivate(volgroup = None): - """Deactivate volume groups by running vgchange -an. - - volgroup - optional single volume group to deactivate - """ - global lvmDevicePresent - if flags.test or lvmDevicePresent == 0: - return - - args = ["vgchange", "-an", "-v"] - if volgroup: - args.append(volgroup) - rc = lvmExec(*args) - if rc: - log.error("running vgchange failed: %s" %(rc,)) -# lvmDevicePresent = 0 - -def lvcreate(lvname, vgname, size): - """Creates a new logical volume. - - lvname - name of logical volume to create. - vgname - name of volume group lv will be in. - size - size of lv, in megabytes. - """ - global lvmDevicePresent - if flags.test or lvmDevicePresent == 0: - return - writeForceConf() - vgscan() - - args = ["lvcreate", "-v", "-L", "%dM" %(size,), "-n", lvname, "-An", vgname] - try: - rc = lvmExec(*args) - except: - rc = 1 - if rc: - raise LVCreateError(vgname, lvname, size) - unlinkConf() - -def lvremove(lvname, vgname): - """Removes a logical volume. - - lvname - name of logical volume to remove. - vgname - name of volume group lv is in. - """ - global lvmDevicePresent - if flags.test or lvmDevicePresent == 0: - return - - args = ["lvremove", "-f", "-v"] - dev = "/dev/%s/%s" %(vgname, lvname) - args.append(dev) - - try: - rc = lvmExec(*args) - except: - rc = 1 - if rc: - raise LVRemoveError(vgname, lvname) - -def lvresize(lvname, vgname, size): - global lvmDevicePresent - if flags.test or lvmDevicePresent == 0: - return - - args = ["lvresize", "-An", "-L", "%dM" %(size,), "-v", "--force", - "/dev/%s/%s" %(vgname, lvname,)] - - try: - rc = lvmExec(*args) - except: - rc = 1 - if rc: - raise LVResizeError(vgname, lvname) - - -def vgcreate(vgname, PESize, nodes): - """Creates a new volume group." - - vgname - name of volume group to create. - PESize - Physical Extent size, in kilobytes. - nodes - LVM Physical Volumes on which to put the new VG. - """ - global lvmDevicePresent - if flags.test or lvmDevicePresent == 0: - return - - # rescan now that we've recreated pvs. ugh. - writeForceConf() - vgscan() - - args = ["vgcreate", "-v", "-An", "-s", "%sk" % (PESize,), vgname ] - args.extend(nodes) - - try: - rc = lvmExec(*args) - except: - rc = 1 - if rc: - raise VGCreateError(vgname, PESize, nodes) - unlinkConf() - -def vgremove(vgname): - """Removes a volume group. Deactivates the volume group first - - vgname - name of volume group. - """ - global lvmDevicePresent - if flags.test or lvmDevicePresent == 0: - return - - # find the Physical Volumes which make up this Volume Group, so we - # can prune and recreate them. - pvs = [] - for pv in pvlist(): - if pv[1] == vgname: - pvs.append(pv[0]) - - # we'll try to deactivate... if it fails, we'll probably fail on - # the removal too... but it's worth a shot - try: - vgdeactivate(vgname) - except: - pass - - args = ["vgremove", "-v", vgname] - - log.info(string.join(args, ' ')) - try: - rc = lvmExec(*args) - except: - rc = 1 - if rc: - raise VGRemoveError, vgname - - # now iterate all the PVs we've just freed up, so we reclaim the metadata - # space. This is an LVM bug, AFAICS. - for pvname in pvs: - args = ["pvremove", "-ff", "-y", "-v", pvname] - - log.info(string.join(args, ' ')) - try: - rc = lvmExec(*args) - except: - rc = 1 - if rc: - raise PVRemoveError, pvname - - args = ["pvcreate", "-ff", "-y", "-v", pvname] - - log.info(string.join(args, ' ')) - try: - rc = lvmExec(*args) - except: - rc = 1 - if rc: - raise PVCreateError, pvname - wipeOtherMetadataFromPV(pvname) - -def pvcreate(node): - """Initializes a new Physical Volume." - - node - path to device node on which to create the new PV." - """ - global lvmDevicePresent - if flags.test or lvmDevicePresent == 0: - return - - # rescan now that we've recreated pvs. ugh. - writeForceConf() - - args = ["pvcreate", "-ff", "-y", "-v", node ] - - try: - rc = lvmExec(*args) - except: - rc = 1 - if rc: - raise PVCreateError, node - unlinkConf() - wipeOtherMetadataFromPV(node) - -def lvlist(): - global lvmDevicePresent - if lvmDevicePresent == 0: - return [] - - lvs = [] - # field names for "options" are in LVM2.2.01.01/lib/report/columns.h - args = ["lvdisplay", "-C", "--noheadings", "--units", "b", - "--nosuffix", "--separator", ":", "--options", - "vg_name,lv_name,lv_size,origin" - ] - lvscanout = iutil.execWithCapture("lvm", args, stderr = "/dev/tty6") - for line in lvmCapture(*args): - try: - (vg, lv, size, origin) = line - size = long(math.floor(long(size) / (1024 * 1024))) - if origin == '': - origin = None - except: - continue - - logmsg = "lv is %s/%s, size of %s" % (vg, lv, size) - if origin: - logmsg += ", snapshot from %s" % (origin,) - log.info(logmsg) - lvs.append( (vg, lv, size, origin) ) - - return lvs - -def pvlist(): - global lvmDevicePresent - if lvmDevicePresent == 0: - return [] - - pvs = [] - args = ["pvdisplay", "-C", "--noheadings", "--units", "b", - "--nosuffix", "--separator", ":", "--options", - "pv_name,vg_name,dev_size" - ] - for line in lvmCapture(*args): - try: - (dev, vg, size) = line - size = long(math.floor(long(size) / (1024 * 1024))) - except: - continue - - if dev.startswith("/dev/dm-"): - from block import dm - try: - sb = os.stat(dev) - (major, minor) = (os.major(sb.st_rdev), os.minor(sb.st_rdev)) - for map in dm.maps(): - if map.dev.major == major and map.dev.minor == minor: - dev = "/dev/mapper/%s" % map.name - break - except: - pass - - log.info("pv is %s in vg %s, size is %s" %(dev, vg, size)) - pvs.append( (dev, vg, size) ) - - return pvs - -def vglist(): - global lvmDevicePresent - if lvmDevicePresent == 0: - return [] - - vgs = [] - args = ["vgdisplay", "-C", "--noheadings", "--units", "b", - "--nosuffix", "--separator", ":", "--options", - "vg_name,vg_size,vg_extent_size,vg_free" - ] - for line in lvmCapture(*args): - try: - (vg, size, pesize, free) = line - size = long(math.floor(long(size) / (1024 * 1024))) - pesize = long(pesize)/1024 - free = math.floor(long(free) / (1024 * 1024)) - except: - continue - log.info("vg %s, size is %s, pesize is %s" %(vg, size, pesize)) - vgs.append( (vg, size, pesize, free) ) - return vgs - -def partialvgs(): - global lvmDevicePresent - if lvmDevicePresent == 0: - return [] - - vgs = [] - args = ["vgdisplay", "-C", "-P", "--noheadings", "--units", "b", - "--nosuffix", "--separator", ":"] - for line in lvmCapture(*args): - try: - (vg, numpv, numlv, numsn, attr, size, free) = line - except: - continue - if attr.find("p") != -1: - log.info("vg %s, attr is %s" %(vg, attr)) - vgs.append(vg) - - return vgs - -# FIXME: this is a hack. we really need to have a --force option. -def unlinkConf(): - lvmroot = "/etc/lvm" - if os.path.exists("%s/lvm.conf" %(lvmroot,)): - os.unlink("%s/lvm.conf" %(lvmroot,)) - -def writeForceConf(): - """Write out an /etc/lvm/lvm.conf that doesn't do much (any?) filtering""" - - lvmroot = "/etc/lvm" - try: - os.unlink("/etc/lvm/.cache") - except: - pass - if not os.path.isdir(lvmroot): - os.mkdir(lvmroot) - - unlinkConf() - - f = open("%s/lvm.conf" %(lvmroot,), "w+") - f.write(""" -# anaconda hacked lvm.conf to avoid filtering breaking things -devices { - sysfs_scan = 0 - md_component_detection = 1 -} -""") - -# FIXME: another hack. we need to wipe the raid metadata since pvcreate -# doesn't -def wipeOtherMetadataFromPV(node): - try: - isys.wipeRaidSB(node) - except Exception, e: - log.critical("error wiping raidsb from %s: %s", node, e) - - - -def getPossiblePhysicalExtents(floor=0): - """Returns a list of integers representing the possible values for - the physical extent of a volume group. Value is in KB. - - floor - size (in KB) of smallest PE we care about. - """ - - possiblePE = [] - curpe = 8 - while curpe <= 16384*1024: - if curpe >= floor: - possiblePE.append(curpe) - curpe = curpe * 2 - - return possiblePE - -def clampLVSizeRequest(size, pe, roundup=0): - """Given a size and a PE, returns the actual size of logical volumne. - - size - size (in MB) of logical volume request - pe - PE size (in KB) - roundup - round sizes up or not - """ - - if roundup: - func = math.ceil - else: - func = math.floor - return (long(func((size*1024L)/pe))*pe)/1024 - -def clampPVSize(pvsize, pesize): - """Given a PV size and a PE, returns the usable space of the PV. - Takes into account both overhead of the physical volume and 'clamping' - to the PE size. - - pvsize - size (in MB) of PV request - pesize - PE size (in KB) - """ - - # we want Kbytes as a float for our math - pvsize *= 1024.0 - return long((math.floor(pvsize / pesize) * pesize) / 1024) - -def getMaxLVSize(pe): - """Given a PE size in KB, returns maximum size (in MB) of a logical volume. - - pe - PE size in KB - """ - - if os.uname()[2][:4]=="2.4.": - return pe*64 #2.4 kernel - LVM1, max size is 2TiB and depends on extent size/count - - else: #newer kernel - LVM2, size limited by number of sectors - if productArch in ("x86_64", "ppc64"): #64bit architectures - return (8*1024*1024*1024*1024) #Max is 8EiB (very large number..) - else: - return (16*1024*1024) #Max is 16TiB - -def safeLvmName(str): - tmp = string.strip(str) - tmp = tmp.replace("/", "_") - tmp = re.sub("[^0-9a-zA-Z._]", "", str) - tmp = tmp.lstrip("_") - - return tmp - -def createSuggestedVGName(partitions, network): - """Given list of partition requests, come up with a reasonable VG name - - partitions - list of requests - """ - - # try to create a volume group name incorporating the hostname - hn = network.hostname - if hn is not None and hn != '': - if hn == 'localhost' or hn == 'localhost.localdomain': - vgtemplate = "VolGroup" - elif hn.find('.') != -1: - hn = safeLvmName(hn) - vgtemplate = "vg_%s" % (hn.split('.')[0].lower(),) - else: - hn = safeLvmName(hn) - vgtemplate = "vg_%s" % (hn.lower(),) - else: - vgtemplate = "VolGroup" - - if not partitions.isVolumeGroupNameInUse(vgtemplate): - return vgtemplate - else: - i = 0 - while 1: - tmpname = "%s%02d" % (vgtemplate, i,) - if not partitions.isVolumeGroupNameInUse(tmpname): - break - - i += 1 - if i > 99: - tmpname = "" - - return tmpname - -def createSuggestedLVName(logreqs): - """Given list of LV requests, come up with a reasonable LV name - - partitions - list of LV requests for this VG - """ - - i = 0 - - lnames = [] - for lv in logreqs: - lnames.append(lv.logicalVolumeName) - - while 1: - tmpname = "LogVol%02d" % (i,) - if (logreqs is None) or (tmpname not in lnames): - break - - i += 1 - if i > 99: - tmpname = "" - - return tmpname - -def getVGUsedSpace(vgreq, requests, diskset): - vgused = 0 - for request in requests.requests: - if request.type == REQUEST_LV and request.volumeGroup == vgreq.uniqueID: - size = int(request.getActualSize(requests, diskset)) - vgused = vgused + size - - - return vgused - -def getVGFreeSpace(vgreq, requests, diskset): - used = getVGUsedSpace(vgreq, requests, diskset) - log.debug("used space is %s" % (used,)) - - total = vgreq.getActualSize(requests, diskset) - log.debug("actual space is %s" % (total,)) - return total - used diff --git a/partRequests.py b/partRequests.py deleted file mode 100644 index 6f95dc4f7..000000000 --- a/partRequests.py +++ /dev/null @@ -1,1057 +0,0 @@ -# -# partRequests.py: partition request objects and management thereof -# -# Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. -# All rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Author(s): Matt Wilson -# Jeremy Katz -# Mike Fulbright -# Harald Hoyer -# - -"""Partition request objects and management thereof.""" - -import parted -import iutil -import string -import os, sys, math - -from constants import * -from flags import * - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import fsset -import raid -import lvm -import partedUtils -import partIntfHelpers - -import logging -log = logging.getLogger("anaconda") - -class DeleteSpec: - """Defines a preexisting partition which is intended to be removed.""" - - def __init__(self, drive, start, end): - """Initializes a DeleteSpec. - - drive is the text form of the drive - start is the start sector of the deleted partition - end is the end sector of the deleted partition - """ - - self.drive = drive - self.start = start - self.end = end - - def __str__(self): - return "drive: %s start: %s end: %s" %(self.drive, self.start, - self.end) - -class DeleteLogicalVolumeSpec: - """Defines a preexisting logical volume which is intended to be removed.""" - - def __init__(self, name, vg): - """Initializes a DeleteLogicalVolumeSpec. - - name is the name of the lv - vg is the name of the volume group - """ - - self.name = name - self.vg = vg - self.deleted = 0 - - def __str__(self): - return "lvname: %s vgname: %s" %(self.name, self.vg) - - def beenDeleted(self): - return self.deleted - - def setDeleted(self, val): - self.deleted = val - -class DeleteVolumeGroupSpec: - """Defines a preexisting volume group which is intended to be removed.""" - - def __init__(self, name): - """Initializes a DeleteVolumeGroupSpec - - name is the name of the volume group - """ - - self.name = name - self.deleted = 0 - - def __str__(self): - return "vgname: %s" %(self.name,) - - def beenDeleted(self): - return self.deleted - - def setDeleted(self, val): - self.deleted = val - -class DeleteRAIDSpec: - """Defines a preexisting RAID device which is intended to be removed.""" - - def __init__(self, minor): - """Initializes a DeleteRAIDSpec. - - minor is the minor of the RAID device being removed - """ - - self.minor = minor - - def __str__(self): - return "minor: %s" %(self.minor,) - -class RequestSpec: - """Generic Request specification.""" - def __init__(self, fstype, size = None, mountpoint = None, format = None, - preexist = 0, fslabel = None, - migrate = None, origfstype = None, - fsprofile = None): - """Create a generic RequestSpec. - - This should probably never be externally used. - """ - - self.fstype = fstype - self.mountpoint = mountpoint - self.size = size - self.format = format - - self.migrate = migrate - self.origfstype = origfstype - self.fslabel = fslabel - self.fsopts = None - self.fsprofile = fsprofile - - self.device = None - """what we currently think the device is""" - - self.uniqueID = None - """uniqueID is an integer and *MUST* be unique.""" - - self.ignoreBootConstraints = 0 - """Booting constraints should be ignored for this request.""" - - self.preexist = preexist - """Did this partition exist before we started playing with things?""" - - self.protected = 0 - """Is this partitiion 'protected', ie does it contain install media.""" - - self.dev = None - """A Device() as defined in fsset.py to correspond to this request.""" - - self.encryption = None - """An optional LUKSDevice() describing block device encryption.""" - - self.targetSize = None - """Size to resize to""" - - self.resizable = False - """Is this a request that can be resized?""" - - def __str__(self): - if self.fstype: - fsname = self.fstype.getName() - else: - fsname = "None" - - str = ("Generic Request -- mountpoint: %(mount)s uniqueID: %(id)s\n" - " type: %(fstype)s format: %(format)s\n" - " device: %(dev)s migrate: %(migrate)s fslabel: %(fslabel)s\n" - " options: '%(fsopts)s'" - " fsprofile: %(fsprofile)s" % - {"mount": self.mountpoint, "id": self.uniqueID, - "fstype": fsname, "format": self.format, - "dev": self.device, "migrate": self.migrate, - "fslabel": self.fslabel, - "fsopts": self.fsopts, "fsprofile": self.fsprofile}) - return str - - def getActualSize(self, partitions, diskset): - """Return the actual size allocated for the request in megabytes.""" - - sys.stderr.write("WARNING: Abstract RequestSpec.getActualSize() called\n") - import traceback - traceback.print_stack() - - def getDevice(self, partitions): - """Return a device to solidify.""" - - sys.stderr.write("WARNING: Abstract RequestSpec.getDevice() called\n") - import traceback - traceback.print_stack() - - def isResizable(self, partitions): - if self.isEncrypted(partitions): # FIXME: can't resize crypted devs yet - return False - return self.resizable and self.fstype is not None and self.fstype.isResizable() - - def isEncrypted(self, partitions, parentOnly = False): - if self.encryption: - return True - return False - - def toEntry(self, partitions): - """Turn a request into a fsset entry and return the entry.""" - device = self.getDevice(partitions) - - # pin down our partitions so that we can reread the table - device.solidify() - - if self.fstype.getName() == "swap": - mountpoint = "swap" - else: - mountpoint = self.mountpoint - - entry = fsset.FileSystemSetEntry(device, mountpoint, self.fstype, - origfsystem=self.origfstype, - options=self.fsopts, - fsprofile=self.fsprofile) - if self.format: - entry.setFormat(self.format) - - if self.migrate: - entry.setMigrate(self.migrate) - - if self.fslabel: - entry.setLabel(self.fslabel) - - if self.targetSize and self.fstype.isResizable(): - entry.setResizeTarget(self.targetSize, self.size) - - return entry - - def setProtected(self, val): - """Set the protected value for this partition.""" - self.protected = val - - def getProtected(self): - """Return the protected value for this partition.""" - return self.protected - - def getPreExisting(self): - """Return whether the partition existed before we started playing.""" - return self.preexist - - def doMountPointLinuxFSChecks(self): - """Return an error string if the mountpoint is not valid for Linux FS.""" - mustbeonroot = ('/bin','/dev','/sbin','/etc','/lib','/root', - '/mnt', 'lost+found', '/proc') - mustbeonlinuxfs = ('/', '/boot', '/var', '/tmp', '/usr', '/home', - '/usr/share', '/usr/lib' ) - - # these are symlinks so you cant make them mount points - otherexcept = ('/var/mail', '/usr/bin/X11', '/usr/lib/X11', '/usr/tmp') - - if not self.mountpoint: - return None - - if self.fstype is None: - return None - - if flags.livecdInstall and self.mountpoint == "/" and not self.format: - return _("The mount point %s must be formatted during live CD " - "installs.") % self.mountpoint - if flags.livecdInstall and self.mountpoint == "/" and self.fstype.getName() not in ["ext4", "ext3", "ext2"]: - return _("The mount point %s must be formatted to match the live " - "rootfs during live CD installs.") % self.mountpoint - - if self.fstype.isMountable(): - if self.mountpoint in mustbeonroot: - return _("This mount point is invalid. The %s directory must " - "be on the / file system.") % (self.mountpoint,) - elif self.mountpoint in otherexcept: - return _("The mount point %s cannot be used. It must " - "be a symbolic link for proper system " - "operation. Please select a different " - "mount point.") % (self.mountpoint,) - - return None - - if not self.fstype.isLinuxNativeFS(): - if self.mountpoint in mustbeonlinuxfs: - return _("This mount point must be on a linux file system.") - - return None - - # requestSkipList is a list of uids for requests to ignore when - # looking for a conflict on the mount point name. Used in lvm - # editting code in disk druid, for example. - def isMountPointInUse(self, partitions, requestSkipList=None): - """Return whether my mountpoint is in use by another request.""" - mntpt = self.mountpoint - if not mntpt: - return None - - if partitions and partitions.requests: - for request in partitions.requests: - if requestSkipList is not None and request.uniqueID in requestSkipList: - continue - - if request.mountpoint == mntpt: - if (not self.uniqueID or - request.uniqueID != self.uniqueID): - return _("The mount point \"%s\" is already in use, " - "please choose a different mount point." - %(mntpt)) - return None - - def doSizeSanityCheck(self): - """Sanity check that the size of the request is sane.""" - if not self.fstype: - return None - - if not self.format: - return None - - if self.size and self.size > self.fstype.getMaxSizeMB(): - return (_("The size of the %s partition (%10.2f MB) " - "exceeds the maximum size of %10.2f MB.") - % (self.fstype.getName(), self.size, - self.fstype.getMaxSizeMB())) - - return None - - # set skipMntPtExistCheck to non-zero if you want to handle this - # check yourself. Used in lvm volume group editting code, for example. - def sanityCheckRequest(self, partitions, skipMntPtExistCheck=0): - """Run the basic sanity checks on the request.""" - # see if mount point is valid if its a new partition request - mntpt = self.mountpoint - fstype = self.fstype - preexist = self.preexist - format = self.format - - rc = self.doSizeSanityCheck() - if rc: - return rc - - rc = partIntfHelpers.sanityCheckMountPoint(mntpt, fstype, preexist, format) - if rc: - return rc - - if not skipMntPtExistCheck: - rc = self.isMountPointInUse(partitions) - if rc: - return rc - - rc = self.doMountPointLinuxFSChecks() - if rc: - return rc - - return None - - - def formatByDefault(self): - """Return whether or not the request should be formatted by default.""" - def inExceptionList(mntpt): - exceptlist = ['/home', '/usr/local', '/opt', '/var/www'] - for q in exceptlist: - if os.path.commonprefix([mntpt, q]) == q: - return 1 - return 0 - - # check first to see if its a Linux filesystem or not - formatlist = ['/boot', '/var', '/tmp', '/usr'] - - if not self.fstype: - return 0 - - if not self.fstype.isLinuxNativeFS(): - return 0 - - if self.fstype.isMountable(): - mntpt = self.mountpoint - if mntpt == "/": - return 1 - - if mntpt in formatlist: - return 1 - - for p in formatlist: - if os.path.commonprefix([mntpt, p]) == p: - if inExceptionList(mntpt): - return 0 - else: - return 1 - - return 0 - else: - if self.fstype.getName() == "swap": - return 1 - - # be safe for anything else and default to off - return 0 - - -# XXX preexistings store start/end as sectors, new store as cylinders. ICK -class PartitionSpec(RequestSpec): - """Object to define a requested partition.""" - - # XXX eep, still a few too many options but a lot better - def __init__(self, fstype, size = None, mountpoint = None, - preexist = 0, migrate = None, grow = 0, maxSizeMB = None, - start = None, end = None, drive = None, primary = None, - format = None, multidrive = None, - fslabel = None, fsprofile=None): - """Create a new PartitionSpec object. - - fstype is the fsset filesystem type. - size is the requested size (in megabytes). - mountpoint is the mountpoint. - grow is whether or not the partition is growable. - maxSizeMB is the maximum size of the partition in megabytes. - start is the starting cylinder/sector (new/preexist). - end is the ending cylinder/sector (new/preexist). - drive is the drive the partition goes on. - primary is whether or not the partition should be forced as primary. - format is whether or not the partition should be formatted. - preexist is whether this partition is preexisting. - migrate is whether or not the partition should be migrated. - multidrive specifies if this is a request that should be replicated - across _all_ of the drives in drive - fslabel is the label to give to the filesystem. - fsprofile is the usage profile for the filesystem. - """ - - # if it's preexisting, the original fstype should be set - if preexist == 1: - origfs = fstype - else: - origfs = None - - RequestSpec.__init__(self, fstype = fstype, size = size, - mountpoint = mountpoint, format = format, - preexist = preexist, migrate = None, - origfstype = origfs, - fslabel = fslabel, fsprofile = fsprofile) - self.type = REQUEST_NEW - - self.grow = grow - self.maxSizeMB = maxSizeMB - self.requestSize = size - self.start = start - self.end = end - - if (type(drive) != type([])) and (drive is not None): - self.drive = [ drive ] - else: - self.drive = drive - - self.primary = primary - self.multidrive = multidrive - - # should be able to map this from the device =\ - self.currentDrive = None - """Drive that this request will currently end up on.""" - - - def __str__(self): - if self.fstype: - fsname = self.fstype.getName() - else: - fsname = "None" - - if self.origfstype: - oldfs = self.origfstype.getName() - else: - oldfs = "None" - - if self.preexist == 0: - pre = "New" - else: - pre = "Existing" - - if self.encryption is None: - crypto = "None" - else: - crypto = self.encryption.getScheme() - - str = ("%(n)s Part Request -- mountpoint: %(mount)s uniqueID: %(id)s\n" - " type: %(fstype)s format: %(format)s \n" - " device: %(dev)s drive: %(drive)s primary: %(primary)s\n" - " size: %(size)s grow: %(grow)s maxsize: %(max)s\n" - " start: %(start)s end: %(end)s migrate: %(migrate)s " - " fslabel: %(fslabel)s origfstype: %(origfs)s\n" - " options: '%(fsopts)s'\n" - " fsprofile: %(fsprofile)s encryption: %(encryption)s" % - {"n": pre, "mount": self.mountpoint, "id": self.uniqueID, - "fstype": fsname, "format": self.format, "dev": self.device, - "drive": self.drive, "primary": self.primary, - "size": self.size, "grow": self.grow, "max": self.maxSizeMB, - "start": self.start, "end": self.end, - "migrate": self.migrate, "fslabel": self.fslabel, - "origfs": oldfs, - "fsopts": self.fsopts, "fsprofile": self.fsprofile, - "encryption": crypto}) - return str - - - def getDevice(self, partitions): - """Return a device to solidify.""" - if self.dev: - return self.dev - - self.dev = fsset.PartitionDevice(self.device, - encryption = self.encryption) - - return self.dev - - def getActualSize(self, partitions, diskset): - """Return the actual size allocated for the request in megabytes.""" - size = 0 - - for drive in self.drive: - part = diskset.disks[drive].getPartitionByPath("/dev/%s" % self.device) - - if part: - size += part.getSize(unit="MB") - - if size == 0: - return self.requestSize - - return size - - def doSizeSanityCheck(self): - """Sanity check that the size of the partition is sane.""" - if not self.fstype: - return None - if not self.format: - return None - ret = RequestSpec.doSizeSanityCheck(self) - if ret is not None: - return ret - - if (self.size and self.maxSizeMB - and (self.size > self.maxSizeMB)): - return (_("The size of the requested partition (size = %s MB) " - "exceeds the maximum size of %s MB.") - % (self.size, self.maxSizeMB)) - - if self.size and self.size < 0: - return _("The size of the requested partition is " - "negative! (size = %s MB)") % (self.size) - - if self.start and self.start < 1: - return _("Partitions can't start below the first cylinder.") - - if self.end and self.end < 1: - return _("Partitions can't end on a negative cylinder.") - - return None - -class NewPartitionSpec(PartitionSpec): - """Object to define a NEW requested partition.""" - - # XXX eep, still a few too many options but a lot better - def __init__(self, fstype, size = None, mountpoint = None, - grow = 0, maxSizeMB = None, - start = None, end = None, - drive = None, primary = None, format = None): - """Create a new NewPartitionSpec object. - - fstype is the fsset filesystem type. - size is the requested size (in megabytes). - mountpoint is the mountpoint. - grow is whether or not the partition is growable. - maxSizeMB is the maximum size of the partition in megabytes. - start is the starting cylinder. - end is the ending cylinder. - drive is the drive the partition goes on. - primary is whether or not the partition should be forced as primary. - format is whether or not the partition should be formatted. - """ - - PartitionSpec.__init__(self, fstype = fstype, size = size, - mountpoint = mountpoint, grow = grow, - maxSizeMB = maxSizeMB, start = start, - end = end, drive = drive, primary = primary, - format = format, preexist = 0) - self.type = REQUEST_NEW - -class PreexistingPartitionSpec(PartitionSpec): - """Request to represent partitions which already existed.""" - - def __init__(self, fstype, size = None, start = None, end = None, - drive = None, format = None, migrate = None, - mountpoint = None): - """Create a new PreexistingPartitionSpec object. - - fstype is the fsset filesystem type. - size is the size (in megabytes). - start is the starting sector. - end is the ending sector. - drive is the drive which the partition is on. - format is whether or not the partition should be formatted. - migrate is whether or not the partition fs should be migrated. - mountpoint is the mountpoint. - """ - - PartitionSpec.__init__(self, fstype = fstype, size = size, - start = start, end = end, drive = drive, - format = format, migrate = migrate, - mountpoint = mountpoint, preexist = 1) - self.type = REQUEST_PREEXIST - self.resizable = True - - self.maxResizeSize = None - """Maximum size of this partition request""" - - def getMaximumResizeMB(self, partitions): - if self.maxResizeSize is not None: - return self.maxResizeSize - log.warning("%s doesn't have a max size set" %(self.device,)) - return MAX_PART_SIZE - - def getMinimumResizeMB(self, partitions): - return self.fstype.getMinimumSize(self.device) - -class RaidRequestSpec(RequestSpec): - """Request to represent RAID devices.""" - - def __init__(self, fstype, format = None, mountpoint = None, - raidlevel = None, raidmembers = None, - raidspares = None, raidminor = None, fslabel = None, - preexist = 0, chunksize = None, - fsprofile = None): - """Create a new RaidRequestSpec object. - - fstype is the fsset filesystem type. - format is whether or not the partition should be formatted. - mountpoint is the mountpoint. - raidlevel is the raidlevel (as 'RAID0', 'RAID1', 'RAID5'). - chunksize is the chunksize which should be used. - raidmembers is list of ids corresponding to the members of the RAID. - raidspares is the number of spares to setup. - raidminor is the minor of the device which should be used. - fslabel is the label of the filesystem. - fsprofile is the usage profile for the filesystem. - """ - - # if it's preexisting, the original fstype should be set - if preexist == 1: - origfs = fstype - else: - origfs = None - - RequestSpec.__init__(self, fstype = fstype, format = format, - mountpoint = mountpoint, preexist = preexist, - origfstype = origfs, - fslabel=fslabel, fsprofile=fsprofile) - self.type = REQUEST_RAID - - - self.raidlevel = raidlevel - self.raidmembers = raidmembers - self.raidspares = raidspares - self.raidminor = raidminor - self.chunksize = chunksize - - def __str__(self): - if self.fstype: - fsname = self.fstype.getName() - else: - fsname = "None" - raidmem = [] - if self.raidmembers: - for i in self.raidmembers: - raidmem.append(i) - - if self.encryption is None: - crypto = "None" - else: - crypto = self.encryption.getScheme() - - str = ("RAID Request -- mountpoint: %(mount)s uniqueID: %(id)s\n" - " type: %(fstype)s format: %(format)s\n" - " raidlevel: %(level)s raidspares: %(spares)s\n" - " raidmembers: %(members)s fsprofile: %(fsprofile)s\n" - " encryption: %(encryption)s" % - {"mount": self.mountpoint, "id": self.uniqueID, - "fstype": fsname, "format": self.format, - "level": self.raidlevel, "spares": self.raidspares, - "members": self.raidmembers, "fsprofile": self.fsprofile, - "encryption": crypto - }) - return str - - def getDevice(self, partitions): - """Return a device which can be solidified.""" - # Alway return a new device for minor changing - raidmems = [] - for member in self.raidmembers: - request = partitions.getRequestByID(member) - raidmems.append(request.getDevice(partitions)) - self.dev = fsset.RAIDDevice(int(self.raidlevel[4:]), - raidmems, minor = self.raidminor, - spares = self.raidspares, - existing = self.preexist, - chunksize = self.chunksize, - encryption = self.encryption) - return self.dev - - def isEncrypted(self, partitions, parentOnly = False): - if RequestSpec.isEncrypted(self, partitions) is True: - return True - if parentOnly: - return False - for member in self.raidmembers: - if partitions.getRequestByID(member).isEncrypted(partitions): - return True - return False - - def getActualSize(self, partitions, diskset): - """Return the actual size allocated for the request in megabytes.""" - - # this seems like a check which should never fail... - if not self.raidmembers or not self.raidlevel: - return 0 - nummembers = len(self.raidmembers) - self.raidspares - smallest = None - sum = 0 - for member in self.raidmembers: - req = partitions.getRequestByID(member) - partsize = req.getActualSize(partitions, diskset) - - if raid.isRaid0(self.raidlevel): - sum = sum + partsize - else: - if not smallest: - smallest = partsize - elif partsize < smallest: - smallest = partsize - - if raid.isRaid0(self.raidlevel): - return sum - elif raid.isRaid1(self.raidlevel): - return smallest - elif raid.isRaid5(self.raidlevel): - return (nummembers-1) * smallest - elif raid.isRaid6(self.raidlevel): - return (nummembers-2) * smallest - elif raid.isRaid10(self.raidlevel): - return (nummembers/2) * smallest - else: - raise ValueError, "Invalid raidlevel in RaidRequest.getActualSize" - - - # do RAID specific sanity checks; this is an internal function - def sanityCheckRaid(self, partitions): - if not self.raidmembers or not self.raidlevel: - return _("No members in RAID request, or not RAID " - "level specified.") - - minmembers = raid.get_raid_min_members(self.raidlevel) - if len(self.raidmembers) < minmembers: - return _("A RAID device of type %s " - "requires at least %s members.") % (self.raidlevel, - minmembers) - - if len(self.raidmembers) > 27: - return "RAID devices are limited to 27 members." - - if self.raidspares: - if (len(self.raidmembers) - self.raidspares) < minmembers: - return _("This RAID device can have a maximum of %s spares. " - "To have more spares you will need to add members to " - "the RAID device.") % (len(self.raidmembers) - - minmembers ) - return None - - def sanityCheckRequest(self, partitions): - """Run the basic sanity checks on the request.""" - rc = self.sanityCheckRaid(partitions) - if rc: - return rc - - return RequestSpec.sanityCheckRequest(self, partitions) - -class VolumeGroupRequestSpec(RequestSpec): - """Request to represent volume group devices.""" - - def __init__(self, fstype =None, format = None, - vgname = None, physvols = None, - pesize = 32768, preexist = 0, - preexist_size = 0): - """Create a new VolumeGroupRequestSpec object. - - fstype is the fsset filesystem type. - format is whether or not the volume group should be created. - vgname is the name of the volume group. - physvols is a list of the ids for the physical volumes in the vg. - pesize is the size of a physical extent in kilobytes. - preexist is whether the volume group is preexisting. - preexist_size is the size of a preexisting VG read from /proc - (note that this is unclamped) - """ - - if not fstype: - fstype = fsset.fileSystemTypeGet("volume group (LVM)") - RequestSpec.__init__(self, fstype = fstype, format = format) - self.type = REQUEST_VG - - self.volumeGroupName = vgname - self.physicalVolumes = physvols - self.pesize = pesize - self.preexist = preexist - self.free = 0 - - # FIXME: this is a hack so that we can set the vg name automagically - # with autopartitioning to not conflict with existing vgs - self.autoname = 0 - - if preexist and preexist_size: - self.preexist_size = preexist_size - else: - self.preexist_size = None - - def __str__(self): - physvols = [] - if self.physicalVolumes: - for i in self.physicalVolumes: - physvols.append(i) - - str = ("VG Request -- name: %(vgname)s uniqueID: %(id)s\n" - " format: %(format)s pesize: %(pesize)s \n" - " physvols: %(physvol)s" % - {"vgname": self.volumeGroupName, "id": self.uniqueID, - "format": self.format, "physvol": physvols, - "pesize": self.pesize}) - return str - - def getDevice(self, partitions): - """Return a device which can be solidified.""" - if self.dev: - # FIXME: this warning can probably be removed post-beta - log.warning("getting self.dev more than once for %s" %(self,)) - return self.dev - - pvs = [] - for pv in self.physicalVolumes: - r = partitions.getRequestByID(pv) - # a size of zero implies we did autopartitioning of - # pvs everywhere we could - if (r.size > 0) or (r.device is not None): - pvs.append(r.getDevice(partitions)) - self.dev = fsset.VolumeGroupDevice(self.volumeGroupName, pvs, - self.pesize, - existing = self.preexist) - return self.dev - - def isEncrypted(self, partitions, parentOnly = False): - if RequestSpec.isEncrypted(self, partitions) is True: - return True - if parentOnly: - return False - for pvid in self.physicalVolumes: - pv = partitions.getRequestByID(pvid) - if pv.isEncrypted(partitions): - return True - return False - - def getActualSize(self, partitions, diskset): - """Return the actual size allocated for the request in megabytes.""" - - # if we have a preexisting size, use it - if self.preexist and self.preexist_size: - totalspace = lvm.clampPVSize(self.preexist_size, self.pesize) - else: - totalspace = 0 - for pvid in self.physicalVolumes: - pvreq = partitions.getRequestByID(pvid) - size = pvreq.getActualSize(partitions, diskset) - #log.info("size for pv %s is %s" % (pvid, size)) - size = lvm.clampPVSize(size, self.pesize) - (self.pesize/1024) - #log.info(" clamped size is %s" % (size,)) - totalspace = totalspace + size - - return totalspace - -class PartialVolumeGroupSpec: - """Request to represent partial volume group devices.""" - # note, these are just used as placeholders so we don't collide on names - - def __init__(self, vgname = None): - """Create a new PartialVolumeGroupSpec object. - - vgname is the name of the volume group. - """ - - self.volumeGroupName = vgname - - def __str__(self): - str = ("Partial VG Request -- name: %(vgname)s" % - {"vgname": self.volumeGroupName}) - return str - -class LogicalVolumeRequestSpec(RequestSpec): - """Request to represent logical volume devices.""" - - def __init__(self, fstype, format = None, mountpoint = None, - size = None, volgroup = None, lvname = None, - preexist = 0, percent = None, grow=0, maxSizeMB=0, - fslabel = None, fsprofile = None): - """Create a new VolumeGroupRequestSpec object. - - fstype is the fsset filesystem type. - format is whether or not the volume group should be created. - mountpoint is the mountpoint for the request. - size is the size of the request in MB. - volgroup is the request ID of the volume group. - lvname is the name of the logical volume. - preexist is whether the logical volume previously existed or not. - percent is the percentage of the volume group's space this should use. - grow is whether or not to use free space remaining. - maxSizeMB is max size to grow to. - fslabel is the label of the filesystem on the logical volume. - fsprofile is the usage profile for the filesystem. - """ - - # if it's preexisting, the original fstype should be set - if preexist == 1: - origfs = fstype - else: - origfs = None - - RequestSpec.__init__(self, fstype = fstype, format = format, - mountpoint = mountpoint, size = size, - preexist = preexist, origfstype = origfs, - fslabel = fslabel, fsprofile = fsprofile) - - self.type = REQUEST_LV - - self.logicalVolumeName = lvname - self.volumeGroup = volgroup - self.percent = percent - self.grow = grow - self.maxSizeMB = maxSizeMB - self.startSize = size - - self.minResizeSize = None - self.resizable = True - - if not percent and not size and not preexist: - raise RuntimeError, "Error with Volume Group:Logical Volume %s:%s - Logical Volume must specify either percentage of vgsize or size" % (volgroup, lvname) - - if percent and grow: - raise RuntimeError, "Error with Volume Group:Logical Volume %s:%s - Logical Volume cannot grow if percentage given" % (volgroup, lvname) - - def __str__(self): - if self.fstype: - fsname = self.fstype.getName() - else: - fsname = "None" - - if self.size is not None: - size = self.size - else: - size = "%s percent" %(self.percent,) - - if self.encryption is None: - crypto = "None" - else: - crypto = self.encryption.getScheme() - - str = ("LV Request -- mountpoint: %(mount)s uniqueID: %(id)s\n" - " type: %(fstype)s format: %(format)s\n" - " size: %(size)s lvname: %(lvname)s volgroup: %(vgid)s\n" - " options: '%(fsopts)s' fsprofile: %(fsprofile)s" - " encryption: '%(crypto)s'" % - {"mount": self.mountpoint, "id": self.uniqueID, - "fstype": fsname, "format": self.format, - "lvname": self.logicalVolumeName, "vgid": self.volumeGroup, - "size": size, "crypto": crypto, - "fsopts": self.fsopts, "fsprofile": self.fsprofile}) - return str - - def getDevice(self, partitions): - """Return a device which can be solidified.""" - vg = partitions.getRequestByID(self.volumeGroup) - vgname = vg.volumeGroupName - self.dev = fsset.LogicalVolumeDevice(vgname, self.size, - self.logicalVolumeName, - vg = vg, - existing = self.preexist, - encryption = self.encryption) - return self.dev - - def isEncrypted(self, partitions, parentOnly = False): - if RequestSpec.isEncrypted(self, partitions) is True: - return True - if parentOnly: - return False - vg = partitions.getRequestByID(self.volumeGroup) - if vg.isEncrypted(partitions): - return True - return False - - def getActualSize(self, partitions = None, diskset = None, target = False): - """Return the actual size allocated for the request in megabytes.""" - if self.percent: - if partitions is None or diskset is None: - raise RuntimeError, "trying to get a percentage lv size on resize path" - vgreq = partitions.getRequestByID(self.volumeGroup) - vgsize = vgreq.getActualSize(partitions, diskset) - lvsize = int(self.percent * 0.01 * vgsize) - #lvsize = lvm.clampLVSizeRequest(lvsize, vgreq.pesize) - return lvsize - # FIXME: the target bit here is a bit of a hack... - elif self.targetSize is not None and target: - return self.targetSize - else: - return self.size - - def getStartSize(self): - """Return the starting size allocated for the request in megabytes.""" - return self.startSize - - def setSize(self, size): - """Set the size (in MB) of request (does not clamp to PE however) - - size - size in MB - """ - if self.percent: - self.percent = None - - self.size = size - - def sanityCheckRequest(self, partitions, skipMntPtExistCheck=0, pesize=32768): - """Run the basic sanity checks on the request.""" - if not self.grow and not self.percent and self.size*1024 < pesize: - return _("Logical volume size must be larger than the volume " - "group's physical extent size.") - - return RequestSpec.sanityCheckRequest(self, partitions, skipMntPtExistCheck) - - def getMaximumResizeMB(self, partitions): - vg = partitions.getRequestByID(self.volumeGroup) - print("max is", self.getActualSize(), vg.free, self.getActualSize() + vg.free) - return self.getActualSize() + vg.free - - def getMinimumResizeMB(self, partitions): - if self.minResizeSize is None: - log.warning("don't know the minimum size of %s" %(self.logicalVolumeName,)) - return 1 - return self.minResizeSize diff --git a/partitions.py b/partitions.py deleted file mode 100644 index 59ad85767..000000000 --- a/partitions.py +++ /dev/null @@ -1,1899 +0,0 @@ -# -# partitions.py: partition object containing partitioning info -# -# Copyright (C) 2002, 2003, 2004, 2005, 2006 Red Hat, Inc. -# All rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Author(s): Matt Wilson -# Jeremy Katz -# Mike Fulbright -# Harald Hoyer -# - -"""Overarching partition object.""" - -import parted -import iutil -import isys -import string -import os -import sys - -from constants import * -from flags import flags -from errors import * - -import fsset -import raid -import lvm -import partedUtils -import partRequests -import cryptodev - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("anaconda") - -# dispatch.py helper function -def partitionObjectsInitialize(anaconda): - # shut down all dm devices - anaconda.id.diskset.closeDevices() - anaconda.id.diskset.stopMdRaid() - anaconda.id.zfcp.shutdown() - - # clean slate about drives - isys.flushDriveDict() - - if anaconda.dir == DISPATCH_BACK: - return - - # make ibft configured iscsi disks available when findrootparts was skipped - anaconda.id.iscsi.startup(anaconda.intf) - - # ensure zfcp devs are up - anaconda.id.zfcp.startup() - - # pull in the new iscsi drive - isys.flushDriveDict() - - # read in drive info - anaconda.id.diskset.refreshDevices() - - anaconda.id.partitions.setFromDisk(anaconda.id.diskset) - anaconda.id.partitions.setProtected(anaconda.dispatch) - -# dispatch.py helper function -def partitioningComplete(anaconda): - if anaconda.dir == DISPATCH_BACK and anaconda.id.fsset.isActive(): - rc = anaconda.intf.messageWindow(_("Installation cannot continue."), - _("The partitioning options you have chosen " - "have already been activated. You can " - "no longer return to the disk editing " - "screen. Would you like to continue " - "with the installation process?"), - type = "yesno") - if rc == 0: - sys.exit(0) - return DISPATCH_FORWARD - - anaconda.id.partitions.sortRequests() - anaconda.id.fsset.reset() - undoEncryption = False - partitions = anaconda.id.partitions - preexist = partitions.hasPreexistingCryptoDev() - for request in anaconda.id.partitions.requests: - # XXX improve sanity checking - if (not request.fstype or (request.fstype.isMountable() - and not request.mountpoint)): - continue - - if request.encryption and request.encryption.format: - if anaconda.isKickstart and request.encryption.hasPassphrase(): - # they set a passphrase for this device explicitly - pass - elif partitions.encryptionPassphrase: - request.encryption.setPassphrase(partitions.encryptionPassphrase) - elif undoEncryption: - request.encryption = None - if request.dev: - request.dev.crypto = None - else: - while True: - (passphrase, retrofit) = anaconda.intf.getLuksPassphrase(preexist=preexist) - if passphrase: - request.encryption.setPassphrase(passphrase) - partitions.encryptionPassphrase = passphrase - partitions.retrofitPassphrase = retrofit - break - else: - rc = anaconda.intf.messageWindow(_("Encrypt device?"), - _("You specified block device encryption " - "should be enabled, but you have not " - "supplied a passphrase. If you do not " - "go back and provide a passphrase, " - "block device encryption will be " - "disabled."), - type="custom", - custom_buttons=[_("Back"), _("Continue")], - default=0) - if rc == 1: - log.info("user elected to not encrypt any devices.") - request.encryption = None - if request.dev: - request.dev.encryption = None - undoEncryption = True - partitions.autoEncrypt = False - break - - entry = request.toEntry(anaconda.id.partitions) - if entry: - anaconda.id.fsset.add (entry) - else: - raise RuntimeError, ("Managed to not get an entry back from " - "request.toEntry") - - if anaconda.isKickstart: - return - - rc = anaconda.intf.messageWindow(_("Writing partitioning to disk"), - _("The partitioning options you have selected " - "will now be written to disk. Any " - "data on deleted or reformatted partitions " - "will be lost."), - type = "custom", custom_icon="warning", - custom_buttons=[_("Go _back"), - _("_Write changes to disk")], - default = 0) - if rc == 0: - return DISPATCH_BACK - -def lookup_cryptodev(device): - for encryptedDev, cdev in Partitions.encryptedDevices.items(): - mappedDev = cdev.getDevice() - if device == encryptedDev or device == mappedDev: - return cdev - -class Partitions: - """Defines all of the partition requests and delete requests.""" - encryptedDevices = {} - - def __init__ (self, anaconda, readDisks=False): - """Initializes a Partitions object. - - Can pass in the diskset if it already exists. - """ - self.anaconda = anaconda - - self.requests = [] - """A list of RequestSpec objects for all partitions.""" - - self.deletes = [] - """A list of DeleteSpec objects for partitions to be deleted.""" - - self.autoPartitionRequests = [] - """A list of RequestSpec objects for autopartitioning. - These are setup by the installclass and folded into self.requests - by auto partitioning.""" - - self.autoClearPartType = CLEARPART_TYPE_NONE - """What type of partitions should be cleared?""" - - self.autoClearPartDrives = None - """Drives to clear partitions on (note that None is equiv to all).""" - - self.nextUniqueID = 1 - """Internal counter. Don't touch unless you're smarter than me.""" - - self.reinitializeDisks = 0 - """Should the disk label be reset on all disks?""" - - self.zeroMbr = 0 - """Should the mbr be zero'd?""" - - self.protected = [] - """A list of partitions that are the installation source for hard - drive or livecd installs. Partitions on this list may not be - formatted.""" - - self.autoEncrypt = False - - self.encryptionPassphrase = "" - self.retrofitPassphrase = False - - # partition method to be used. not to be touched externally - self.useAutopartitioning = 1 - self.useFdisk = 0 - - # autopartitioning info becomes kickstart partition requests - # and its useful to be able to differentiate between the two - self.isKickstart = 0 - - if readDisks: - self.anaconda.id.diskset.refreshDevices() - self.setFromDisk(self.anaconda.id.diskset) - - def protectedPartitions(self): - return self.protected - - def hasPreexistingCryptoDev(self): - rc = False - for request in self.requests: - if request.encryption and request.encryption.format == 0: - rc = True - break - - return rc - - def getCryptoDev(self, device): - log.info("going to get passphrase for encrypted device %s" % device) - luksDev = self.encryptedDevices.get(device) - if luksDev: - log.debug("passphrase for device %s already known" % device) - return luksDev - - intf = self.anaconda.intf - luksDev = cryptodev.LUKSDevice(device) - if self.encryptionPassphrase: - luksDev.setPassphrase(self.encryptionPassphrase) - if not luksDev.openDevice(): - self.encryptedDevices[device] = luksDev - return luksDev - else: - luksDev.setPassphrase("") - - if intf is None: - return - - buttons = [_("Back"), _("Continue")] - devname = os.path.basename(device) - while True: - (passphrase, isglobal) = intf.passphraseEntryWindow(devname) - if not passphrase: - rc = intf.messageWindow(_("Confirm"), - _("Are you sure you want to skip " - "entering a passphrase for device " - "%s?\n\n" - "If you skip this step the " - "device's contents will not " - "be available during " - "installation.") % devname, - type = "custom", - default = 0, - custom_buttons = buttons) - if rc == 0: - continue - else: - log.info("skipping passphrase for %s" % (device,)) - break - - luksDev.setPassphrase(passphrase) - rc = luksDev.openDevice() - if rc: - luksDev.setPassphrase("") - continue - else: - self.encryptedDevices[device] = luksDev - if isglobal: - self.encryptionPassphrase = passphrase - break - - return self.encryptedDevices.get(device) - - def getEncryptedDevices(self, diskset): - """ find and obtain passphrase for any encrypted devices """ - drives = diskset.disks.keys() - drives.sort() - for drive in drives: - if diskset.anaconda.isKickstart and \ - ((self.autoClearPartType != CLEARPART_TYPE_NONE and \ - (not self.autoClearPartDrives or \ - drive in self.autoClearPartDrives)) or \ - drive in diskset.skippedDisks): - continue - - disk = diskset.disks[drive] - for part in disk.partitions: - if part.type & parted.PARTITION_METADATA: - continue - - device = part.getDeviceNodeName() - fs = isys.readFSType("/dev/%s" % (device,)) - if fs and fs.endswith("raid"): - continue - - if cryptodev.isLuks("/dev/%s" % device): - self.getCryptoDev(device) - - diskset.startMPath() - diskset.startDmRaid() - diskset.startMdRaid() - mdList = diskset.mdList - for raidDev in mdList: - (theDev, devices, level, numActive) = raidDev - if cryptodev.isLuks("/dev/%s" % theDev): - self.getCryptoDev(theDev) - - lvm.writeForceConf() - # now to read in pre-existing LVM stuff - lvm.vgscan() - lvm.vgactivate() - - for (vg, size, pesize, vgfree) in lvm.vglist(): - for (lvvg, lv, size, lvorigin) in lvm.lvlist(): - if lvorigin: - continue - if lvvg != vg: - continue - - theDev = "/dev/mapper/%s-%s" %(vg, lv) - if cryptodev.isLuks(theDev): - self.getCryptoDev("mapper/%s-%s" % (vg, lv)) - - lvm.vgdeactivate() - diskset.stopMdRaid() - for luksDev in self.encryptedDevices.values(): - luksDev.closeDevice() - # try again now that encryption mappings are closed - lvm.vgdeactivate() - diskset.stopMdRaid() - for luksDev in self.encryptedDevices.values(): - luksDev.closeDevice() - - # We shouldn't have any further need for the global passphrase - # except for new device creation, in which case we want to give - # the user a chance to establish a new global passphrase. - self.encryptionPassphrase = "" - - def setFromDisk(self, diskset): - """Clear the delete list and set self.requests to reflect disk.""" - self.deletes = [] - self.requests = [] - if diskset.anaconda.isKickstart and not diskset.anaconda.id.upgrade: - self.getEncryptedDevices(diskset) - labels = diskset.getInfo() - drives = diskset.disks.keys() - drives.sort() - for drive in drives: - disk = diskset.disks[drive] - part = disk.getFirstPartition() - while part: - if (part.type & parted.PARTITION_METADATA) or \ - (part.type & parted.PARTITION_FREESPACE) or \ - (part.type & parted.PARTITION_PROTECTED): - part = part.nextPartition() - continue - - format = None - if part.type & parted.PARTITION_EXTENDED: - ptype = None - elif part.getFlag(parted.PARTITION_RAID) == 1: - ptype = fsset.fileSystemTypeGet("software RAID") - elif part.getFlag(parted.PARTITION_LVM) == 1: - ptype = fsset.fileSystemTypeGet("physical volume (LVM)") - else: - ptype = partedUtils.get_partition_file_system_type(part) - - # FIXME: we don't handle ptype being None very well, so - # just say it's foreign. Should probably fix None - # handling instead some day. - if ptype is None: - ptype = fsset.fileSystemTypeGet("foreign") - - device = part.getDeviceNodeName() - - # parted doesn't tell ext4 from ext3 - if ptype == fsset.fileSystemTypeGet("ext3"): - fsname = isys.readFSType("/dev/%s" % (device,)) - try: - ptype = fsset.fileSystemTypeGet(fsname) - except: - ptype = fsset.fileSystemTypeGet("foreign") - - luksDev = self.encryptedDevices.get(device) - if luksDev and not luksDev.openDevice(): - mappedDev = luksDev.getDevice() - fsname = isys.readFSType("/dev/%s" % (mappedDev,)) - try: - ptype = fsset.fileSystemTypeGet(fsname) - except: - ptype = fsset.fileSystemTypeGet("foreign") - elif cryptodev.isLuks("/dev/%s" % device): - ptype = fsset.fileSystemTypeGet("foreign") - - start = part.geometry.start - end = part.geometry.end - size = part.getSize(unit="MB") - drive = partedUtils.get_partition_drive(part) - - spec = partRequests.PreexistingPartitionSpec(ptype, - size = size, - start = start, - end = end, - drive = drive, - format = format) - spec.device = fsset.PartedPartitionDevice(part).getDevice() - spec.encryption = luksDev - spec.maxResizeSize = part.getMaxAvailableSize(unit="MB") - - # set label if makes sense - if ptype and ptype.isMountable(): - if spec.device in labels.keys(): - if labels[spec.device] and len(labels[spec.device])>0: - spec.fslabel = labels[spec.device] - elif luksDev and not luksDev.getStatus() and mappedDev in labels.keys(): - if labels[mappedDev] and len(labels[mappedDev])>0: - spec.fslabel = labels[mappedDev] - self.addRequest(spec) - - part = part.nextPartition() - - # now we need to read in all pre-existing RAID stuff - diskset.startMPath() - diskset.startDmRaid() - diskset.startMdRaid() - mdList = diskset.mdList - for raidDev in mdList: - (theDev, devices, level, numActive) = raidDev - level = "RAID%s" %(level,) - - if level not in raid.availRaidLevels: - log.warning("raid level %s not supported, skipping %s" %(level, - theDev)) - continue - - try: - chunk = isys.getRaidChunkFromDevice("/dev/%s" %(devices[0],)) - except Exception, e: - log.error("couldn't get chunksize of %s: %s" %(theDev, e)) - chunk = None - - # is minor always mdN ? - minor = int(theDev[2:]) - raidvols = [] - for dev in devices: - req = self.getRequestByDeviceName(dev) - if not req: - log.error("RAID device %s using non-existent partition %s" - %(theDev, dev)) - continue - raidvols.append(req.uniqueID) - - - luksDev = self.encryptedDevices.get(theDev) - if luksDev and not luksDev.openDevice(): - device = luksDev.getDevice() - else: - device = theDev - - fs = isys.readFSType("/dev/%s" %(device,)) - try: - fsystem = fsset.fileSystemTypeGet(fs) - except: - fsystem = fsset.fileSystemTypeGet("foreign") - - try: - fslabel = isys.readFSLabel(device) - except: - fslabel = None - - mnt = None - format = 0 - - spares = len(devices) - numActive - spec = partRequests.RaidRequestSpec(fsystem, format = format, - raidlevel = level, - raidmembers = raidvols, - raidminor = minor, - raidspares = spares, - mountpoint = mnt, - preexist = 1, - chunksize = chunk, - fslabel = fslabel) - spec.size = spec.getActualSize(self, diskset) - spec.encryption = luksDev - self.addRequest(spec) - - lvm.writeForceConf() - # now to read in pre-existing LVM stuff - lvm.vgscan() - lvm.vgactivate() - - pvs = lvm.pvlist() - for (vg, size, pesize, vgfree) in lvm.vglist(): - try: - preexist_size = float(size) - except: - log.error("preexisting size for %s not a valid integer, ignoring" %(vg,)) - preexist_size = None - - pvids = [] - for (dev, pvvg, size) in pvs: - if vg != pvvg: - continue - req = self.getRequestByDeviceName(dev[5:]) - if not req: - log.error("Volume group %s using non-existent partition %s" - %(vg, dev)) - continue - pvids.append(req.uniqueID) - spec = partRequests.VolumeGroupRequestSpec(format = 0, - vgname = vg, - physvols = pvids, - pesize = pesize, - preexist = 1, - preexist_size = preexist_size) - spec.free = vgfree - vgid = self.addRequest(spec) - - for (lvvg, lv, size, lvorigin) in lvm.lvlist(): - if lvorigin: - continue - if lvvg != vg: - continue - - # size is number of bytes, we want size in megs - lvsize = float(size) - - theDev = "/dev/%s/%s" %(vg, lv) - - luksDev = self.encryptedDevices.get("mapper/%s-%s" % (vg, lv)) - if luksDev and not luksDev.openDevice(): - device = luksDev.getDevice() - else: - device = theDev - - fs = isys.readFSType(device) - fslabel = None - - try: - fsystem = fsset.fileSystemTypeGet(fs) - except: - fsystem = fsset.fileSystemTypeGet("foreign") - - try: - fslabel = isys.readFSLabel(device) - except: - fslabel = None - - mnt = None - format = 0 - - spec = partRequests.LogicalVolumeRequestSpec(fsystem, - format = format, size = lvsize, volgroup = vgid, - lvname = lv, mountpoint = mnt, fslabel = fslabel, - preexist = 1) - if fsystem.isResizable(): - spec.minResizeSize = fsystem.getMinimumSize("%s/%s" %(vg, lv)) - spec.encryption = luksDev - self.addRequest(spec) - - for vg in lvm.partialvgs(): - spec = partRequests.PartialVolumeGroupSpec(vgname = vg) - self.addDelete(spec) - - lvm.vgdeactivate() - diskset.stopMdRaid() - for luksDev in self.encryptedDevices.values(): - luksDev.closeDevice() - - # try again now that encryption mappings are closed - lvm.vgdeactivate() - diskset.stopMdRaid() - for luksDev in self.encryptedDevices.values(): - luksDev.closeDevice() - - def addRequest (self, request): - """Add a new request to the list.""" - if not request.uniqueID: - request.uniqueID = self.nextUniqueID - self.nextUniqueID = self.nextUniqueID + 1 - self.requests.append(request) - self.requests.sort() - - return request.uniqueID - - def addDelete (self, delete): - """Add a new DeleteSpec to the list.""" - self.deletes.append(delete) - self.deletes.sort() - - def removeRequest (self, request): - """Remove a request from the list.""" - self.requests.remove(request) - - def getRequestByMountPoint(self, mount): - """Find and return the request with the given mountpoint.""" - for request in self.requests: - if request.mountpoint == mount: - return request - - for request in self.requests: - if request.type == REQUEST_LV and request.mountpoint == mount: - return request - return None - - def getRequestByDeviceName(self, device): - """Find and return the request with the given device name.""" - if device is None: - return None - - for request in self.requests: - if request.type == REQUEST_RAID and request.raidminor is not None: - tmp = "md%d" % (request.raidminor,) - if tmp == device: - return request - elif request.device == device: - return request - elif request.encryption: - deviceUUID = cryptodev.luksUUID("/dev/" + device) - cryptoDev = request.encryption.getDevice() - cryptoUUID = request.encryption.getUUID() - if cryptoDev == device or \ - (cryptoUUID and cryptoUUID == deviceUUID): - return request - return None - - - def getRequestsByDevice(self, diskset, device): - """Find and return the requests on a given device (like 'hda').""" - if device is None: - return None - - drives = diskset.disks.keys() - if device not in drives: - return None - - rc = [] - disk = diskset.disks[device] - for part in disk.partitions: - dev = part.getDeviceNodeName() - request = self.getRequestByDeviceName(dev) - - if request: - rc.append(request) - - if len(rc) > 0: - return rc - else: - return None - - def getRequestByVolumeGroupName(self, volname): - """Find and return the request with the given volume group name.""" - if volname is None: - return None - - for request in self.requests: - if (request.type == REQUEST_VG and - request.volumeGroupName == volname): - return request - return None - - def getRequestByLogicalVolumeName(self, lvname): - """Find and return the request with the given logical volume name.""" - if lvname is None: - return None - for request in self.requests: - if (request.type == REQUEST_LV and - request.logicalVolumeName == lvname): - return request - return None - - def getRequestByID(self, id): - """Find and return the request with the given unique ID. - - Note that if id is a string, it will be converted to an int for you. - """ - if type(id) == type("a string"): - id = int(id) - for request in self.requests: - if request.uniqueID == id: - return request - return None - - def getUnderlyingRequests(self, request): - loop = True - requests = [ ] - requests.append(request) - - # loop over requests descending the storage stack until we - # are at the bottom - while loop: - loop_requests = requests - requests = [ ] - loop = False - - for request in loop_requests: - ids = [ ] - - if (request.type == REQUEST_NEW or \ - request.type == REQUEST_PREEXIST): - requests.append(request) - elif (request.type == REQUEST_RAID): - if request.raidmembers: - ids = request.raidmembers - else: - requests.append(request) - elif (request.type == REQUEST_VG): - ids = request.physicalVolumes - elif (request.type == REQUEST_LV): - ids.append(request.volumeGroup) - else: - log.error("getUnderlyingRequests unknown request type") - - for id in ids: - tmpreq = self.getRequestByID(id) - if tmpreq: - requests.append(tmpreq) - loop = True - else: - log.error("getUnderlyingRequests could not get request for id %s" % (id,)) - - return requests - - def getRaidRequests(self): - """Find and return a list of all of the RAID requests.""" - retval = [] - for request in self.requests: - if request.type == REQUEST_RAID: - retval.append(request) - - return retval - - def getRaidDevices(self): - """Find and return a list of all of the requests for use in RAID.""" - raidRequests = [] - for request in self.requests: - if isinstance(request, partRequests.RaidRequestSpec): - raidRequests.append(request) - - return raidRequests - - def getAvailableRaidMinors(self): - """Find and return a list of all of the unused minors for use in RAID.""" - raidMinors = range(0,32) - for request in self.requests: - if isinstance(request, partRequests.RaidRequestSpec) and request.raidminor in raidMinors: - raidMinors.remove(request.raidminor) - - return raidMinors - - - def getAvailRaidPartitions(self, request, diskset): - """Return a list of tuples of RAID partitions which can be used. - - Return value is (part, size, used) where used is 0 if not, - 1 if so, 2 if used for *this* request. - """ - rc = [] - drives = diskset.disks.keys() - raiddevs = self.getRaidDevices() - drives.sort() - for drive in drives: - disk = diskset.disks[drive] - for part in disk.getRaidPartitions(): - partname = part.getDeviceNodeName() - used = 0 - for raid in raiddevs: - if raid.raidmembers: - for raidmem in raid.raidmembers: - tmpreq = self.getRequestByID(raidmem) - if (partname == tmpreq.device): - if raid.uniqueID == request.uniqueID: - used = 2 - else: - used = 1 - break - if used: - break - size = part.getSize(unit="MB") - - if not used: - rc.append((partname, size, 0)) - elif used == 2: - rc.append((partname, size, 1)) - - return rc - - def getRaidMemberParent(self, request): - """Return RAID device request containing this request.""" - raiddev = self.getRaidRequests() - if not raiddev or not request.device: - return None - for dev in raiddev: - if not dev.raidmembers: - continue - for member in dev.raidmembers: - if request.device == self.getRequestByID(member).device: - return dev - return None - - def isRaidMember(self, request): - """Return whether or not the request is being used in a RAID device.""" - if self.getRaidMemberParent(request) is not None: - return 1 - else: - return 0 - - def getLVMLVForVGID(self, vgid): - """Find and return a list of all the LVs associated with a VG id.""" - retval = [] - for request in self.requests: - if request.type == REQUEST_LV: - if request.volumeGroup == vgid: - retval.append(request) - return retval - - def getLVMLVForVG(self, vgrequest): - """Find and return a list of all of the LVs in the VG.""" - vgid = vgrequest.uniqueID - return self.getLVMLVForVGID(vgid) - - def getLVMRequests(self): - """Return a dictionary of all of the LVM bits. - - The dictionary returned is of the form vgname: [ lvrequests ] - """ - retval = {} - for request in self.requests: - if request.type == REQUEST_VG: - retval[request.volumeGroupName] = self.getLVMLVForVG(request) - - return retval - - def getPartialLVMRequests(self): - """Return a list of all of the partial volume groups names.""" - retval = [] - for request in self.deletes: - if isinstance(request, partRequests.PartialVolumeGroupSpec): - retval.append(request.volumeGroupName) - - return retval - - def getLVMVGRequests(self): - """Find and return a list of all of the volume groups.""" - retval = [] - for request in self.requests: - if request.type == REQUEST_VG: - retval.append(request) - - return retval - - def getLVMLVRequests(self): - """Find and return a list of all of the logical volumes.""" - retval = [] - for request in self.requests: - if request.type == REQUEST_LV: - retval.append(request) - - return retval - - def getAvailLVMPartitions(self, request, diskset): - """Return a list of tuples of PV partitions which can be used. - - Return value is (part, size, used) where used is 0 if not, - 1 if so, 2 if used for *this* request. - """ - rc = [] - drives = diskset.disks.keys() - drives.sort() - volgroups = self.getLVMVGRequests() - pvlist = lvm.pvlist() - for drive in drives: - disk = diskset.disks[drive] - for part in disk.getLVMPartitions(): - partname = part.getDeviceNodeName() - partrequest = self.getRequestByDeviceName(partname) - if partrequest.encryption is None and \ - cryptodev.isLuks("/dev/%s" % partname) and \ - not self.encryptedDevices.get(partname): - log.debug("ignoring PV %s since we cannot access it's contents" % partname) - # We don't want to treat an encrypted PV like a PV if the - # user chose not to provide a passphrase for this device. - # However, if the LUKS device belongs to a just-deleted - # request then we know it is available. - continue - used = 0 - for volgroup in volgroups: - if volgroup.physicalVolumes: - if partrequest.uniqueID in volgroup.physicalVolumes: - if (request and request.uniqueID and - volgroup.uniqueID == request.uniqueID): - used = 2 - else: - used = 1 - - if used: - break - size = None - for pvpart, pvvg, pvsize in pvlist: - if pvpart == "/dev/%s" % (partname,): - size = pvsize - if size is None: - # if we get here, there's no PV data in the partition, - # so clamp the partition's size to 64M - size = part.getSize(unit="MB") - size = lvm.clampPVSize(size, 65536) - - if used == 0: - rc.append((partrequest.uniqueID, size, 0)) - elif used == 2: - rc.append((partrequest.uniqueID, size, 1)) - - # now find available RAID devices - raiddev = self.getRaidRequests() - if raiddev: - raidcounter = 0 - for dev in raiddev: - used = 0 - - if dev.fstype is None: - continue - if dev.fstype.getName() != "physical volume (LVM)": - continue - - for volgroup in volgroups: - if volgroup.physicalVolumes: - if dev.uniqueID in volgroup.physicalVolumes: - if (request and request.uniqueID and - volgroup.uniqueID == request.uniqueID): - used = 2 - else: - used = 1 - - if used: - break - - size = dev.getActualSize(self, diskset) - - if used == 0: - rc.append((dev.uniqueID, size, 0)) - elif used == 2: - rc.append((dev.uniqueID, size, 1)) - - raidcounter = raidcounter + 1 - return rc - - def getLVMVolumeGroupMemberParent(self, request): - """Return parent volume group of a physical volume""" - volgroups = self.getLVMVGRequests() - if not volgroups: - return None - - for volgroup in volgroups: - if volgroup.physicalVolumes: - if request.uniqueID in volgroup.physicalVolumes: - return volgroup - - return None - - def isLVMVolumeGroupMember(self, request): - """Return whether or not the request is being used in an LVM device.""" - if self.getLVMVolumeGroupMemberParent(request) is None: - return 0 - else: - return 1 - - def isVolumeGroupNameInUse(self, vgname): - """Return whether or not the requested volume group name is in use.""" - if not vgname: - return None - - lvmrequests = self.getLVMRequests() - if lvmrequests: - if vgname in lvmrequests.keys(): - return 1 - - lvmrequests = self.getPartialLVMRequests() - if lvmrequests: - if vgname in lvmrequests: - return 1 - - return 0 - - def getBootableRequest(self): - """Return the name of the current 'boot' mount point.""" - bootreq = None - - if iutil.isEfi(): - ret = None - for req in self.requests: - if req.fstype == fsset.fileSystemTypeGet("efi"): - ret = [ req ] - if ret: - req = self.getRequestByMountPoint("/boot") - if req: - ret.append(req) - return ret - elif iutil.getPPCMachine() == "iSeries": - for req in self.requests: - if req.fstype == fsset.fileSystemTypeGet("PPC PReP Boot"): - return [ req ] - return None - elif (iutil.getPPCMachine() == "pSeries"): - # pSeries and Mac bootable requests are odd. - # have to consider both the PReP or Bootstrap partition (with - # potentially > 1 existing) as well as /boot,/ - - ret = [] - for req in self.requests: - if req.fstype == fsset.fileSystemTypeGet("PPC PReP Boot"): - ret.append(req) - - # now add the /boot - bootreq = self.getRequestByMountPoint("/boot") - if not bootreq: - bootreq = self.getRequestByMountPoint("/") - if bootreq: - ret.append(bootreq) - - if len(ret) >= 1: - return ret - return None - elif (iutil.getPPCMachine() == "PMac"): - # for the bootstrap partition, we want either the first or the - # first non-preexisting one - bestprep = None - for req in self.requests: - if req.fstype == fsset.fileSystemTypeGet("Apple Bootstrap"): - if ((bestprep is None) or - (bestprep.getPreExisting() and - not req.getPreExisting())): - bestprep = req - - if bestprep: - ret = [ bestprep ] - else: - ret = [] - - # now add the /boot - bootreq = self.getRequestByMountPoint("/boot") - if not bootreq: - bootreq = self.getRequestByMountPoint("/") - if bootreq: - ret.append(bootreq) - - if len(ret) >= 1: - return ret - return None - - if not bootreq: - bootreq = self.getRequestByMountPoint("/boot") - if not bootreq: - bootreq = self.getRequestByMountPoint("/") - - if bootreq: - return [ bootreq ] - return None - - def getBootableMountpoints(self): - """Return a list of bootable valid mountpoints for this arch.""" - # FIXME: should be somewhere else, preferably some sort of arch object - - if iutil.isEfi(): - return [ "/boot/efi" ] - else: - return [ "/boot", "/" ] - - def isBootable(self, request): - """Returns if the request should be considered a 'bootable' request. - - This basically means that it should be sorted to the beginning of - the drive to avoid cylinder problems in most cases. - """ - bootreqs = self.getBootableRequest() - if not bootreqs: - return 0 - - for bootreq in bootreqs: - if bootreq == request: - return 1 - - if bootreq.type == REQUEST_RAID and \ - request.uniqueID in bootreq.raidmembers: - return 1 - - return 0 - - def sortRequests(self): - """Resort the requests into allocation order.""" - n = 0 - while n < len(self.requests): - # Ignore LVM Volume Group and Logical Volume requests, - # since these are not related to allocating disk partitions - if (self.requests[n].type == REQUEST_VG or self.requests[n].type == REQUEST_LV): - n = n + 1 - continue - - for request in self.requests: - # Ignore LVM Volume Group and Logical Volume requests, - # since these are not related to allocating disk partitions - if (request.type == REQUEST_VG or request.type == REQUEST_LV): - continue - # for raid requests, the only thing that matters for sorting - # is the raid device since ordering by size is mostly - # irrelevant. this also keeps things more consistent - elif (request.type == REQUEST_RAID or - self.requests[n].type == REQUEST_RAID): - if (request.type == self.requests[n].type and - (self.requests[n].raidminor != None) and - ((request.raidminor is None) or - request.raidminor > self.requests[n].raidminor)): - tmp = self.requests[n] - index = self.requests.index(request) - self.requests[n] = request - self.requests[index] = tmp - # for sized requests, we want the larger ones first - elif (request.size and self.requests[n].size and - (request.size < self.requests[n].size)): - tmp = self.requests[n] - index = self.requests.index(request) - self.requests[n] = request - self.requests[index] = tmp - # for cylinder-based, sort by order on the drive - elif (request.start and self.requests[n].start and - (request.drive in self.requests[n].drive) and - (request.type == self.requests[n].type) and - (request.start > self.requests[n].start)): - tmp = self.requests[n] - index = self.requests.index(request) - self.requests[n] = request - self.requests[index] = tmp - # finally just use when they defined the partition so - # there's no randomness thrown in - elif (request.size and self.requests[n].size and - (request.size == self.requests[n].size) and - (request.uniqueID < self.requests[n].uniqueID)): - tmp = self.requests[n] - index = self.requests.index(request) - self.requests[n] = request - self.requests[index] = tmp - n = n + 1 - - tmp = self.getBootableRequest() - - boot = [] - if tmp: - for req in tmp: - # if raid, we want all of the contents of the bootable raid - if req.type == REQUEST_RAID: - for member in req.raidmembers: - boot.append(self.getRequestByID(member)) - else: - boot.append(req) - - # remove the bootables from the request - for bootable in boot: - self.requests.pop(self.requests.index(bootable)) - - # move to the front of the list - boot.extend(self.requests) - self.requests = boot - - def sanityCheckAllRequests(self, diskset, baseChecks = 0): - """Do a sanity check of all of the requests. - - This function is called at the end of partitioning so that we - can make sure you don't have anything silly (like no /, a really - small /, etc). Returns (errors, warnings) where each is a list - of strings or None if there are none. - If baseChecks is set, the basic sanity tests which the UI runs prior to - accepting a partition will be run on the requests as well. - """ - checkSizes = [('/usr', 250), ('/tmp', 50), ('/var', 384), - ('/home', 100), ('/boot', 75)] - warnings = [] - errors = [] - - slash = self.getRequestByMountPoint('/') - if not slash: - errors.append(_("You have not defined a root partition (/), " - "which is required for installation of %s " - "to continue.") % (productName,)) - - if slash and slash.getActualSize(self, diskset) < 250: - warnings.append(_("Your root partition is less than 250 " - "megabytes which is usually too small to " - "install %s.") % (productName,)) - - if (slash and self.anaconda and - (slash.getActualSize(self, diskset) < - self.anaconda.backend.getMinimumSizeMB("/"))): - errors.append(_("Your %s partition is less than %s " - "megabytes which is lower than recommended " - "for a normal %s install.") - %("/", self.anaconda.backend.getMinimumSizeMB("/"), - productName)) - - def getBaseReqs(reqs): - n = 0 - while not reduce(lambda x,y: x and (y.type not in [REQUEST_RAID, REQUEST_LV]), - reqs, True) \ - and len(reqs) > n: - req = reqs[n] - if req.type == REQUEST_RAID: - for id in req.raidmembers: - reqs.append(self.getRequestByID(id)) - del reqs[n] - continue - elif req.type == REQUEST_LV: - del reqs[n] - continue - n += 1 - return reqs - - if iutil.isEfi(): - bootreq = self.getRequestByMountPoint("/boot/efi") - if (not bootreq) or \ - bootreq.fstype != fsset.fileSystemTypeGet("efi") or \ - bootreq.getActualSize(self, diskset) < 10: - errors.append(_("You must create an EFI System Partition of " - "at least 10 megabytes.")) - elif iutil.isX86(): - if iutil.isMactel(): - # mactel checks - bootreqs = self.getBootableRequest() or [] - for br in getBaseReqs(bootreqs): - if not isinstance(br, partRequests.PartitionSpec): - continue - (dev, num) = fsset.getDiskPart(br.device) - - if partedUtils.hasGptLabel(diskset, dev) \ - and int(num) > 4: - errors.append( - _("Your boot partition isn't on one of " - "the first four partitions and thus " - "won't be bootable.")) - - if iutil.getPPCMacGen() == "NewWorld": - reqs = self.getBootableRequest() - found = 0 - - bestreq = None - if reqs: - for req in reqs: - if req.fstype == fsset.fileSystemTypeGet("Apple Bootstrap"): - found = 1 - # the best one is either the first or the first - # newly formatted one - if ((bestreq is None) or ((bestreq.format == 0) and - (req.format == 1))): - bestreq = req - break - - if not found: - errors.append(_("You must create an Apple Bootstrap partition.")) - - if (iutil.getPPCMachine() == "pSeries" or - iutil.getPPCMachine() == "iSeries"): - reqs = self.getBootableRequest() - found = 0 - - bestreq = None - if reqs: - for req in reqs: - if req.fstype == fsset.fileSystemTypeGet("PPC PReP Boot"): - found = 1 - # the best one is either the first or the first - # newly formatted one - if ((bestreq is None) or ((bestreq.format == 0) and - (req.format == 1))): - bestreq = req - break - if iutil.getPPCMachine() == "iSeries" and iutil.hasiSeriesNativeStorage(): - found = 1 - - if not found: - errors.append(_("You must create a PPC PReP Boot partition.")) - - if bestreq is not None: - if (iutil.getPPCMachine() == "pSeries"): - minsize = 2 - else: - minsize = 12 - if bestreq.getActualSize(self, diskset) < minsize: - warnings.append(_("Your %s partition is less than %s " - "megabytes which is lower than " - "recommended for a normal %s install.") - %(_("PPC PReP Boot"), minsize, productName)) - - - for (mount, size) in checkSizes: - req = self.getRequestByMountPoint(mount) - if not req: - continue - if req.getActualSize(self, diskset) < size: - warnings.append(_("Your %s partition is less than %s " - "megabytes which is lower than recommended " - "for a normal %s install.") - %(mount, size, productName)) - - foundSwap = 0 - swapSize = 0 - usesUSB = False - usesFireWire = False - - for request in self.requests: - if request.fstype and request.fstype.getName() == "swap": - foundSwap = foundSwap + 1 - swapSize = swapSize + request.getActualSize(self, diskset) - if baseChecks: - rc = request.doSizeSanityCheck() - if rc: - warnings.append(rc) - rc = request.doMountPointLinuxFSChecks() - if rc: - errors.append(rc) - if isinstance(request, partRequests.RaidRequestSpec): - rc = request.sanityCheckRaid(self) - if rc: - errors.append(rc) - if not hasattr(request,'drive'): - continue - for x in request.drive or []: - if isys.driveUsesModule(x, ["usb-storage", "ub"]): - usesUSB = True - elif isys.driveUsesModule(x, ["sbp2", "firewire-sbp2"]): - usesFireWire = True - - if usesUSB: - warnings.append(_("Installing on a USB device. This may " - "or may not produce a working system.")) - if usesFireWire: - warnings.append(_("Installing on a FireWire device. This may " - "or may not produce a working system.")) - - bootreqs = self.getBootableRequest() - if bootreqs: - for bootreq in bootreqs: - if not bootreq: - continue - if (isinstance(bootreq, partRequests.RaidRequestSpec) and - not raid.isRaid1(bootreq.raidlevel)): - errors.append(_("Bootable partitions can only be on RAID1 " - "devices.")) - - # can't have bootable partition on LV - if (isinstance(bootreq, partRequests.LogicalVolumeRequestSpec)): - errors.append(_("Bootable partitions cannot be on a " - "logical volume.")) - - # most arches can't have boot on RAID - if (isinstance(bootreq, partRequests.RaidRequestSpec) and - iutil.getArch() not in raid.raidBootArches): - errors.append(_("Bootable partitions cannot be on a RAID " - "device.")) - - # Lots of filesystems types don't support /boot. - if (bootreq.fstype and not bootreq.fstype.isBootable()): - errors.append(_("Bootable partitions cannot be on an %s " - "filesystem.")%(bootreq.fstype.getName(),)) - - # vfat /boot is insane. - if (bootreq.mountpoint and bootreq.mountpoint == "/" and - bootreq.fstype and bootreq.fstype.getName() == "vfat"): - errors.append(_("Bootable partitions cannot be on an %s " - "filesystem.")%(bootreq.fstype.getName(),)) - - if (bootreq.isEncrypted(self)): - errors.append(_("Bootable partitions cannot be on an " - "encrypted block device")) - - if foundSwap == 0: - warnings.append(_("You have not specified a swap partition. " - "Although not strictly required in all cases, " - "it will significantly improve performance for " - "most installations.")) - - # XXX number of swaps not exported from kernel and could change - if foundSwap >= 32: - warnings.append(_("You have specified more than 32 swap devices. " - "The kernel for %s only supports 32 " - "swap devices.") % (productName,)) - - mem = iutil.memInstalled() - rem = mem % 16384 - if rem: - mem = mem + (16384 - rem) - mem = mem / 1024 - - if foundSwap and (swapSize < (mem - 8)) and (mem < 1024): - warnings.append(_("You have allocated less swap space (%dM) than " - "available RAM (%dM) on your system. This " - "could negatively impact performance.") - %(swapSize, mem)) - - if warnings == []: - warnings = None - if errors == []: - errors = None - - return (errors, warnings) - - def setProtected(self, dispatch): - """Set any partitions which should be protected to be so.""" - for device in self.protectedPartitions(): - log.info("%s is a protected partition" % (device)) - request = self.getRequestByDeviceName(device) - if request is not None: - request.setProtected(1) - else: - log.info("no request, probably a removable drive") - - def copy (self): - """Deep copy the object.""" - new = Partitions(self.anaconda) - for request in self.requests: - new.addRequest(request) - for delete in self.deletes: - new.addDelete(delete) - new.autoPartitionRequests = self.autoPartitionRequests - new.autoClearPartType = self.autoClearPartType - new.autoClearPartDrives = self.autoClearPartDrives - new.nextUniqueID = self.nextUniqueID - new.useAutopartitioning = self.useAutopartitioning - new.useFdisk = self.useFdisk - new.reinitializeDisks = self.reinitializeDisks - new.protected = self.protected - return new - - def getClearPart(self): - """Get the kickstart directive related to the clearpart being used.""" - clearpartargs = [] - if self.autoClearPartType == CLEARPART_TYPE_LINUX: - clearpartargs.append('--linux') - elif self.autoClearPartType == CLEARPART_TYPE_ALL: - clearpartargs.append('--all') - else: - return None - - if self.reinitializeDisks: - clearpartargs.append('--initlabel') - - if self.autoClearPartDrives: - drives = string.join(self.autoClearPartDrives, ',') - clearpartargs.append('--drives=%s' % (drives)) - - return "#clearpart %s\n" %(string.join(clearpartargs)) - - def writeKS(self, f): - """Write out the partitioning information in kickstart format.""" - f.write("# The following is the partition information you requested\n") - f.write("# Note that any partitions you deleted are not expressed\n") - f.write("# here so unless you clear all partitions first, this is\n") - f.write("# not guaranteed to work\n") - clearpart = self.getClearPart() - if clearpart: - f.write(clearpart) - - # lots of passes here -- parts, raid, volgroup, logvol - # XXX what do we do with deleted partitions? - for request in self.requests: - args = [] - if request.type == REQUEST_RAID: - continue - - # no fstype, no deal (same with foreigns) - if not request.fstype or request.fstype.getName() == "foreign": - continue - - # first argument is mountpoint, which can also be swap or - # the unique RAID identifier. I hate kickstart partitioning - # syntax. a lot. too many special cases - if request.fstype.getName() == "swap": - args.append("swap") - elif request.fstype.getName() == "software RAID": - # since we guarantee that uniqueIDs are ints now... - args.append("raid.%s" % (request.uniqueID)) - elif request.fstype.getName() == "physical volume (LVM)": - # see above about uniqueIDs being ints - args.append("pv.%s" % (request.uniqueID)) - elif request.fstype.getName() == "PPC PReP Boot": - args.extend(["prepboot", "--fstype", "\"PPC PReP Boot\""]) - elif request.fstype.getName() == "Apple Bootstrap": - args.extend(["appleboot", "--fstype", "\"Apple Bootstrap\""]) - elif request.mountpoint: - args.extend([request.mountpoint, "--fstype", - request.fstype.getName(quoted = 1)]) - else: - continue - - # generic options - if not request.format: - args.append("--noformat") - - # device encryption - if request.encryption: - args.append("--encrypted") - - # preexisting only - if request.type == REQUEST_PREEXIST and request.device: - args.append("--onpart") - args.append(request.device) - # we have a billion ways to specify new partitions - elif request.type == REQUEST_NEW: - if request.size: - args.append("--size=%s" % (int(request.size),)) - if request.size == 0: - args.append("--size=0") - if request.grow: - args.append("--grow") - if request.start: - args.append("--start=%s" % (request.start)) - if request.end: - args.append("--end=%s" % (request.end)) - if request.maxSizeMB: - args.append("--maxsize=%s" % (request.maxSizeMB)) - if request.drive: - args.append("--ondisk=%s" % (request.drive[0])) - if request.primary: - args.append("--asprimary") - else: # how the hell did we get this? - continue - - f.write("#part %s\n" % (string.join(args))) - - - for request in self.requests: - args = [] - if request.type != REQUEST_RAID: - continue - - # no fstype, no deal (same with foreigns) - if not request.fstype or request.fstype.getName() == "foreign": - continue - - # also require a raidlevel and raidmembers for raid - if (request.raidlevel == None) or not request.raidmembers: - continue - - # first argument is mountpoint, which can also be swap - if request.fstype.getName() == "swap": - args.append("swap") - elif request.fstype.getName() == "physical volume (LVM)": - # see above about uniqueIDs being ints - args.append("pv.%s" % (request.uniqueID)) - elif request.mountpoint: - args.append(request.mountpoint) - else: - continue - - # generic options - if not request.format: - args.append("--noformat") - if request.preexist: - args.append("--useexisting") - if request.fstype: - args.extend(["--fstype", request.fstype.getName(quoted = 1)]) - - # device encryption - if request.encryption: - args.append("--encrypted") - - args.append("--level=%s" % (request.raidlevel)) - args.append("--device=md%s" % (request.raidminor)) - - if request.raidspares: - args.append("--spares=%s" % (request.raidspares)) - - # silly raid member syntax - raidmems = [] - for member in request.raidmembers: - if (type(member) != type("")) or (member[0:5] != "raid."): - raidmems.append("raid.%s" % (member)) - else: - raidmems.append(member) - args.append("%s" % (string.join(raidmems))) - - f.write("#raid %s\n" % (string.join(args))) - - for request in self.requests: - args = [] - if request.type != REQUEST_VG: - continue - - args.append(request.volumeGroupName) - - # generic options - if not request.format: - args.append("--noformat") - if request.preexist: - args.append("--useexisting") - - args.append("--pesize=%s" %(request.pesize,)) - - # silly pv syntax - pvs = [] - for member in request.physicalVolumes: - if (type(member) != type("")) or not member.startswith("pv."): - pvs.append("pv.%s" % (member)) - else: - pvs.append(member) - args.append("%s" % (string.join(pvs))) - - f.write("#volgroup %s\n" % (string.join(args))) - - for request in self.requests: - args = [] - if request.type != REQUEST_LV: - continue - - # no fstype, no deal (same with foreigns) - if not request.fstype or request.fstype.getName() == "foreign": - continue - - # require a vg name and an lv name - if (request.logicalVolumeName is None or - request.volumeGroup is None): - continue - - # first argument is mountpoint, which can also be swap - if request.fstype.getName() == "swap": - args.append("swap") - elif request.mountpoint: - args.append(request.mountpoint) - else: - continue - - # generic options - if not request.format: - args.append("--noformat") - if request.preexist: - args.append("--useexisting") - if request.fstype: - args.extend(["--fstype", request.fstype.getName(quoted = 1)]) - - # device encryption - if request.encryption: - args.append("--encrypted") - - vg = self.getRequestByID(request.volumeGroup) - if vg is None: - continue - - args.extend(["--name=%s" %(request.logicalVolumeName,), - "--vgname=%s" %(vg.volumeGroupName,)]) - - if request.grow: - if request.startSize: - args.append("--size=%s" % (int(request.startSize),)) - else: - # shouldnt happen - continue - - args.append("--grow") - if request.maxSizeMB and int(request.maxSizeMB) > 0: - args.append("--maxsize=%s" % (request.maxSizeMB,)) - else: - if request.percent: - args.append("--percent=%s" %(request.percent,)) - elif request.size: - args.append("--size=%s" %(int(request.size),)) - else: - continue - - f.write("#logvol %s\n" % (string.join(args))) - - def deleteAllLogicalPartitions(self, part): - """Add delete specs for all logical partitions in part.""" - for partition in part.disk.getLogicalPartitions(): - partName = partition.getDeviceNodeName() - request = self.getRequestByDeviceName(partName) - self.removeRequest(request) - if request.preexist: - drive = partedUtils.get_partition_drive(partition) - delete = partRequests.DeleteSpec(drive, - partition.geometry.start, - partition.geometry.end) - self.addDelete(delete) - - def containsImmutablePart(self, part): - """Returns whether the partition contains parts we can't delete.""" - if not part or (type(part) == type("RAID")) or (type(part) == type(1)): - return None - - if not part.type & parted.PARTITION_EXTENDED: - return None - - disk = part.disk - while part: - if not part.active: - part = disk.nextPartition(part) - continue - - device = part.getDeviceNodeName() - request = self.getRequestByDeviceName(device) - - if request: - if request.getProtected(): - return _("the partition in use by the installer.") - - if self.isRaidMember(request): - return _("a partition which is a member of a RAID array.") - - if self.isLVMVolumeGroupMember(request): - return _("a partition which is a member of a LVM Volume Group.") - - part = disk.nextPartition(part) - return None - - - def doMetaDeletes(self, diskset): - """Does the removal of all of the non-physical volumes in the delete list.""" - - # have to have lvm on, which requires raid to be started - diskset.startMPath() - diskset.startDmRaid() - diskset.startMdRaid() - for luksDev in self.encryptedDevices.values(): - luksDev.openDevice() - lvm.vgactivate() - - snapshots = {} - for (lvvg, lv, size, lvorigin) in lvm.lvlist(): - snapshots.setdefault(lv, []) - if lvorigin: - snapshots.setdefault(lvorigin, []) - snapshots[lvorigin].append((lv, lvvg)) - - lvm_parent_deletes = [] - tmp = {} - def addSnap(name, vg): - if not snapshots.has_key(name): - return - snaps = snapshots[name] - for snap, snapvg in snaps: - addSnap(snap, snapvg) - if not tmp.has_key((name, vg)): - tmp[(name, vg)] = 1 - lvm_parent_deletes.append((name,vg)) - - # now, go through and delete logical volumes - for delete in self.deletes: - if isinstance(delete, partRequests.DeleteLogicalVolumeSpec): - if not delete.beenDeleted(): - addSnap(delete.name, delete.vg) - delete.setDeleted(1) - - for name,vg in lvm_parent_deletes: - log.info("removing lv %s" % (name,)) - key = "mapper/%s-%s" % (vg, name) - if key in self.encryptedDevices.keys(): - self.encryptedDevices[key].closeDevice() - del self.encryptedDevices[key] - lvm.lvremove(name, vg) - - # now, go through and delete volume groups - for delete in self.deletes: - if isinstance(delete, partRequests.DeleteVolumeGroupSpec): - if not delete.beenDeleted(): - lvm.vgremove(delete.name) - delete.setDeleted(1) - - lvm.vgdeactivate() - - # now, remove obsolete cryptodev instances - for (device, luksDev) in self.encryptedDevices.items(): - luksDev.closeDevice() - found = 0 - for req in self.requests: - if req.encryption == luksDev: - found = 1 - - if not found: - del self.encryptedDevices[device] - - diskset.stopMdRaid() - - def doMetaResizes(self, diskset): - """Does resizing of non-physical volumes.""" - - # have to have lvm on, which requires raid to be started - devicesActive = diskset.devicesOpen - if not devicesActive: - # should this not be diskset.openDevices() ? - diskset.startMPath() - diskset.startDmRaid() - diskset.startMdRaid() - - for luksDev in self.encryptedDevices.values(): - luksDev.openDevice() - lvm.vgactivate() - - # we only support resizing LVM of these types of things currently - for lv in self.getLVMLVRequests(): - if not lv.preexist: - continue - if lv.targetSize is None: - continue - - vg = self.getRequestByID(lv.volumeGroup) - if vg is None: - continue - - lvm.lvresize(lv.logicalVolumeName, vg.volumeGroupName, - lv.targetSize) - - lvm.vgdeactivate() - for luksDev in self.encryptedDevices.values(): - luksDev.closeDevice() - - if not devicesActive: - # should this not be diskset.closeDevices() ? - diskset.stopMdRaid() - diskset.stopDmRaid() - diskset.stopMPath() - - def doEncryptionRetrofits(self): - if not self.retrofitPassphrase or not self.encryptionPassphrase: - return - - for request in self.requests: - if not request.encryption: - continue - - # XXX this will only work before the new LUKS devices are created - # since the format flag gets unset when they are formatted - if request.encryption.format: - continue - - if request.encryption.addPassphrase(self.encryptionPassphrase): - log.error("failed to add new passphrase to existing device %s" % (request.encryption.getDevice(encrypted=1),)) - - def deleteDependentRequests(self, request): - """Handle deletion of this request and all requests which depend on it. - - eg, delete all logical volumes from a volume group, all volume groups - which depend on the raid device. - - Side effects: removes all dependent requests from self.requests - adds needed dependent deletes to self.deletes - """ - - toRemove = [] - id = request.uniqueID - for req in self.requests: - if isinstance(req, partRequests.RaidRequestSpec): - if id in req.raidmembers: - toRemove.append(req) - # XXX do we need to do anything special with preexisting raids? - elif isinstance(req, partRequests.VolumeGroupRequestSpec): - if id in req.physicalVolumes: - toRemove.append(req) - if req.getPreExisting(): - delete = partRequests.DeleteVolumeGroupSpec(req.volumeGroupName) - self.addDelete(delete) - elif isinstance(req, partRequests.LogicalVolumeRequestSpec): - if id == req.volumeGroup: - toRemove.append(req) - tmp = self.getRequestByID(req.volumeGroup) - if not tmp: - log.error("Unable to find the vg for %s" - % (req.logicalVolumeName,)) - vgname = req.volumeGroup - else: - vgname = tmp.volumeGroupName - - if req.getPreExisting(): - delete = partRequests.DeleteLogicalVolumeSpec(req.logicalVolumeName, - vgname) - self.addDelete(delete) - - for req in toRemove: - self.deleteDependentRequests(req) - self.removeRequest(req) - diff --git a/raid.py b/raid.py deleted file mode 100644 index c0f2e692f..000000000 --- a/raid.py +++ /dev/null @@ -1,228 +0,0 @@ -# -# raid.py - raid probing control -# -# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007 -# Red Hat, Inc. All rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Author(s): Erik Troan -# - -"""Raid probing control.""" - -def getRaidLevels(): - avail = [] - try: - f = open("/proc/mdstat", "r") - except: - pass - else: - for l in f.readlines(): - if not l.startswith("Personalities"): - continue - - lst = l.split() - - for lev in ["RAID0", "RAID1", "RAID5", "RAID6", "RAID10"]: - if "[" + lev + "]" in lst or "[" + lev.lower() + "]" in lst: - avail.append(lev) - - f.close() - - avail.sort() - return avail - -# XXX define availraidlevels and defaultmntpts as arch characteristics -availRaidLevels = getRaidLevels() - -import parted -import isys -import os -import partitions -import partedUtils - -import logging -log = logging.getLogger("anaconda") - -# these arches can have their /boot on RAID and not have their -# boot loader blow up -raidBootArches = [ "i386", "x86_64", "ppc" ] - -def scanForRaid(drives): - """Scans for raid devices on drives. - - drives is a list of device names. - Returns a list of (mdMinor, devices, level, totalDisks) tuples. - """ - - raidSets = {} - raidDevices = {} - - for d in drives: - parts = [] - try: - dev = parted.getDevice("/dev/%s" % (d,)) - disk = parted.Disk(device=dev) - - raidParts = disk.getRaidPartitions() - for part in raidParts: - # if the part is encrypted, add the mapped dev instead - pname = part.getDeviceNodeName() - cryptoDev = partitions.Partitions.encryptedDevices.get(pname) - if cryptoDev and not cryptoDev.openDevice(): - dev = cryptoDev.getDevice() - else: - dev = pname - parts.append(dev) - except: - pass - - for dev in parts: - try: - (major, minor, raidSet, level, nrDisks, totalDisks, mdMinor) =\ - isys.raidsb(dev) - except ValueError: - # bad magic, this can't be part of our raid set - log.error("reading raid sb failed for %s",dev) - continue - - if raidSets.has_key(raidSet): - (knownLevel, knownDisks, knownMinor, knownDevices) = \ - raidSets[raidSet] - if knownLevel != level or knownDisks != totalDisks or \ - knownMinor != mdMinor: - # Raise hell - log.error("raid set inconsistency for md%d: " - "all drives in this raid set do not " - "agree on raid parameters. Skipping raid device", - mdMinor) - continue - knownDevices.append(dev) - raidSets[raidSet] = (knownLevel, knownDisks, knownMinor, - knownDevices) - else: - raidSets[raidSet] = (level, totalDisks, mdMinor, [dev,]) - - if raidDevices.has_key(mdMinor): - if (raidDevices[mdMinor] != raidSet): - log.error("raid set inconsistency for md%d: " - "found members of multiple raid sets " - "that claim to be md%d. Using only the first " - "array found.", mdMinor, mdMinor) - continue - else: - raidDevices[mdMinor] = raidSet - - raidList = [] - for key in raidSets.keys(): - (level, totalDisks, mdMinor, devices) = raidSets[key] - if len(devices) < totalDisks: - log.warning("missing components of raid device md%d. The " - "raid device needs %d drive(s) and only %d (was/were) " - "found. This raid device will not be started.", mdMinor, - totalDisks, len(devices)) - continue - raidList.append((mdMinor, devices, level, totalDisks)) - - return raidList - -def startAllRaid(driveList): - """Do a raid start on raid devices and return a list like scanForRaid.""" - rc = [] - mdList = scanForRaid(driveList) - for mdDevice, deviceList, level, numActive in mdList: - devName = "md%d" % (mdDevice,) - isys.raidstart(devName, deviceList[0]) - rc.append((devName, deviceList, level, numActive)) - return rc - -def stopAllRaid(mdList): - """Do a raid stop on each of the raid device tuples given.""" - for dev, devices, level, numActive in mdList: - isys.raidstop(dev) - -def isRaid10(raidlevel): - """Return whether raidlevel is a valid descriptor of RAID10.""" - if raidlevel in ("RAID10", "10", 10): - return True - return False - -def isRaid6(raidlevel): - """Return whether raidlevel is a valid descriptor of RAID6.""" - if raidlevel in ("RAID6", "6", 6): - return True - return False - -def isRaid5(raidlevel): - """Return whether raidlevel is a valid descriptor of RAID5.""" - if raidlevel in ("RAID5", "5", 5): - return True - return False - -def isRaid1(raidlevel): - """Return whether raidlevel is a valid descriptor of RAID1.""" - if raidlevel in ("mirror", "RAID1", "1", 1): - return True - return False - -def isRaid0(raidlevel): - """Return whether raidlevel is a valid descriptor of RAID0.""" - if raidlevel in ("stripe", "RAID0", "0", 0): - return True - return False - -def get_raid_min_members(raidlevel): - """Return the minimum number of raid members required for raid level""" - if isRaid0(raidlevel): - return 2 - elif isRaid1(raidlevel): - return 2 - elif isRaid5(raidlevel): - return 3 - elif isRaid6(raidlevel): - return 4 - elif isRaid10(raidlevel): - return 4 - else: - raise ValueError, "invalid raidlevel in get_raid_min_members" - -def get_raid_max_spares(raidlevel, nummembers): - """Return the maximum number of raid spares for raidlevel.""" - if isRaid0(raidlevel): - return 0 - elif isRaid1(raidlevel) or isRaid5(raidlevel) or isRaid6(raidlevel) or isRaid10(raidlevel): - return max(0, nummembers - get_raid_min_members(raidlevel)) - else: - raise ValueError, "invalid raidlevel in get_raid_max_spares" - -def register_raid_device(mdname, newdevices, newlevel, newnumActive): - """Register a new RAID device in the mdlist.""" - for dev, devices, level, numActive in partedUtils.DiskSet.mdList: - if mdname == dev: - if (devices != newdevices or level != newlevel or numActive != newnumActive): - raise ValueError, "%s is already in the mdList!" % (mdname,) - else: - return - partedUtils.DiskSet.mdList.append((mdname, newdevices[:], newlevel, - newnumActive)) - -def lookup_raid_device(mdname): - """Return the requested RAID device information.""" - for dev, devices, level, numActive in partedUtils.DiskSet.mdList: - if mdname == dev: - return (dev, devices, level, numActive) - raise KeyError, "md device not found" - - diff --git a/storage/__init__.py b/storage/__init__.py new file mode 100644 index 000000000..e44a8b825 --- /dev/null +++ b/storage/__init__.py @@ -0,0 +1,1369 @@ +# __init__.py +# Entry point for anaconda's storage configuration module. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# + +import os +import time + +import sys +sys.path.append("formats") + +if __name__ == "__main__": + import storage_log + +import iutil +from partedUtils import getReleaseString, productMatches +from errors import * +from devices import * +from deviceaction import * +from deviceformat import getFormat +from devicelibs.lvm import safeLvmName + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("storage") + +def storageInitialize(anaconda): + anaconda.storage.shutdown() + + if anaconda.dir == DISPATCH_BACK: + return + + anaconda.storage.reset() + +# dispatch.py helper function +def storageComplete(anaconda): + if anaconda.dir == DISPATCH_BACK and anaconda.id.storage.fsset.isActive: + rc = anaconda.intf.messageWindow(_("Installation cannot continue."), + _("The storage configuration you have " + "chosen has 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 + + if anaconda.isKickstart: + return + + rc = anaconda.intf.messageWindow(_("Writing storage configuration 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 + + +class Storage(object): + def __init__(self, anaconda): + self.anaconda = anaconda + + # storage configuration variables + self.ignoredDisks = [] + self.exclusiveDisks = [] + self.doAutoPart = None + self.clearPartType = None + self.clearPartDisks = [] + self.encryptedAutoPart = None + self.encryptionPassphrase = None + self.encryptionRetrofit = None + self.reinitializeDisks = None + self.zeroMbr = None + self.protectedPartitions = [] + self.autoPartitionRequests = [] + + self.__luksDevs = {} + + self.iscsi = iscsi.iscsi() + self.zfcp = zfcp.ZFCP() + + self._nextID = 0 + self.defaultFSType = get_default_filesystem_type() + + @property + def nextID(self): + id = self._nextID + self._nextID += 1 + return id + + def shutdown(self): + try: + self.deviceTree.teardownAll() + except Exception as e: + log.error("failure tearing down device tree: %s" % e) + + self.zfcp.shutdown() + + # TODO: iscsi.shutdown() + + def reset(self): + """ Reset storage configuration to reflect actual system state. + + This should rescan from scratch but not clobber user-obtained + information like passphrases, iscsi config, &c + + """ + # save passphrases for luks devices so we don't have to reprompt + for device in self.devices: + if device.format.type == "luks" and device.format.exists: + self.__luksDevs[device.format.uuid] = device.format.__passphrase + self.iscsi.startup(self.anaconda.intf) + self.zfcp.startup() + self.devicetree = DeviceTree(intf=self.anaconda.intf, + ignored=self.ignoredDisks, + exclusive=self.exclusiveDisks, + zeroMbr=self.zeroMbr, + passphrase=self.encrpytionPassphrase, + luksDict=self._luksDevs) + self.fsset = FSSet(self.devicetree) + + @property + def devices(self): + """ A list of all the devices in the device tree. """ + devices = self.devicetree.devices.values() + devices.sort(key=lambda d: d.path) + return devices + + @property + def disks(self): + """ A list of the disks in the device tree. + + Ignored disks are not included. + + This is based on the current state of the device tree and + does not necessarily reflect the actual on-disk state of the + system's disks. + """ + disks = self.devicetree.getDevicesByType("disk") + disks.sort(key=lambda d: d.name) + return disks + + @property + def partitions(self): + """ A list of the partitions in the device tree. + + This is based on the current state of the device tree and + does not necessarily reflect the actual on-disk state of the + system's disks. + """ + partitions = self.devicetree.getDevicesByType("partition") + partitions.sort(key=lambda d: d.name) + return partitions + + @property + def vgs(self): + """ A list of the LVM Volume Groups in the device tree. + + This is based on the current state of the device tree and + does not necessarily reflect the actual on-disk state of the + system's disks. + """ + vgs = self.devicetree.getDevicesByType("lvmvg") + vgs.sort(key=lambda d: d.name) + return vgs + + @property + def lvs(self): + """ A list of the LVM Logical Volumes in the device tree. + + This is based on the current state of the device tree and + does not necessarily reflect the actual on-disk state of the + system's disks. + """ + lvs = self.devicetree.getDevicesByType("lvmlv") + lvs.sort(key=lambda d: d.name) + return lvs + + @property + def pvs(self): + """ A list of the LVM Physical Volumes in the device tree. + + This is based on the current state of the device tree and + does not necessarily reflect the actual on-disk state of the + system's disks. + """ + devices = self.devicetree.devices.values() + pvs = [d for d in devices if d.format.type == "lvmpv"] + pvs.sort(key=lambda d: d.name) + return pvs + + def unusedPVs(self, vg=None): + unused = [] + for pv in self.pvs: + used = False + for _vg in self.vgs: + if _vg.dependsOn(pv) and _vg != vg: + used = True + break + elif _vg == vg: + break + if not used: + unused.append(pv) + return unused + + @property + def mdarrays(self): + """ A list of the MD arrays in the device tree. + + This is based on the current state of the device tree and + does not necessarily reflect the actual on-disk state of the + system's disks. + """ + arrays = self.devicetree.getDevicesByType("mdarray") + arrays.sort(key=lambda d: d.name) + return arrays + + @properties + def mdmembers(self): + """ A list of the MD member devices in the device tree. + + This is based on the current state of the device tree and + does not necessarily reflect the actual on-disk state of the + system's disks. + """ + devices = self.devicetree.devices.values() + members = [d for d in devices if d.format.type == "mdmember"] + members.sort(key=lambda d: d.name) + return members + + def unusedMDMembers(self, array=None): + unused = [] + for member in self.mdmembers: + used = False + for _array in self.mdarrays: + if _array.dependsOn(member) and _array != array: + used = True + break + elif _array == array: + break + if not used: + unused.append(member) + return unused + + @property + def unusedMDMinors(self): + """ Return a list of unused minors for use in RAID. """ + raidMinors = range(0,32) + for array in self.mdarrays: + if array.minor is not None: + raidMinors.remove(array.minor) + return raidMinors + + def exceptionDisks(self): + """ Return a list of removable devices to save exceptions to. + + FIXME: This raises the problem that the device tree can be + in a state that does not reflect that actual current + state of the system at any given point. + + We need a way to provide direct scanning of disks, + partitions, and filesystems without relying on the + larger objects' correctness. + + Also, we need to find devices that have just been made + available for the purpose of storing the exception + report. + """ + dests = [] + for device in self.devices: + if not device.removable: + continue + + dev = parted.Device(path=device.path) + disk = parted.Disk(device=dev) + for part in disk.partitions: + if part.active and \ + not part.getFlag(parted.PARTITION_RAID) and \ + not part.getFlag(parted.PARTITION_LVM) and \ + part.fileSystemType in ("ext3", "ext2", "fat16", "fat32"): + dests.append(part.path, device.name) + + if not parts: + dests.append(device.path, device.name) + + return dests + + def deviceImmutable(self, device): + """ Return any reason the device cannot be modified/removed. + + Return False if the device can be removed. + + Devices that cannot be removed include: + + - protected partitions + - devices that are part of an md array or lvm vg + - extended partition containing logical partitions that + meet any of the above criteria + + """ + if not isinstance(device, Device): + raise ValueError("arg1 (%s) must be a Device instance" % device) + + if device.name in self.protectedPartitions: + return _("This partition is holding the data for the hard " + "drive install.") + elif device.type == "partition" and device.isProtected: + # LDL formatted DASDs always have one partition, you'd have to + # reformat the DASD in CDL mode to get rid of it + return _("You cannot delete a partition of a LDL formatted " + "DASD.") + elif device.format.type == "mdmember": + for array in self.mdarrays: + if array.dependsOn(device): + if array.minor is not None: + return _("This device is part of the RAID " + "device %.") % (array.path,) + else: + return _("This device is part of a RAID device.") + elif device.format.type == "lvmpv": + for vg in self.vgs: + if vg.dependsOn(device): + if vg.name is not None: + return _("This device is part of the LVM " + "volume group '%s'.") % (vg.name,) + else: + return _("This device is part of a LVM volume " + "group.") + elif device.type == "partition" and device.isExtended: + reasons = {} + for dep in self.deviceDeps(device): + reason = self.deviceImmutable(dep) + if reason: + reasons[dep.path] = reason + if reasons: + msg = _("This device is an extended partition which " + "contains logical partitions that cannot be " + "deleted:\n\n") + for dev in reasons: + msg += "%s: %s" % (dev, reasons[dev]) + return msg + + return False + + def deviceDeps(self, device): + return self.devicetree.getDependentDevices(device) + + def newPartition(self, fmt_type=None, size=None, disks=[]): + """ Return a new PartitionDevice instance for configuring. """ + return PartitionDevice("req%d" % self.nextID, + format=getFormat(fmt_type), + size=size, + parent=disks, + exists=False) + + def newMDArray(self, fmt_type=None): + """ Return a new MDRaidArrayDevice instance for configuring. """ + minor = self.unusedMDMinors[0] + return MDRaidArrayDevice("md%d" % minor, + minor=minor, + format=getFormat(fmt_type)) + + def newVG(self, name=None, pvs=[], peSize=None): + """ Return a new LVMVolumeGroupDevice instance. """ + for pv in pvs: + if not pv in self.devices: + raise ValueError("pv is not in the device tree") + + if name and name in [d.name for d in self.devices]: + raise ValueError("name already in use") + elif not name: + name = self.createSuggestedVGName(self.anaconda.id.network) + + return LVMVolumeGroupDevice(name, parents=pvs, peSize=peSize) + + def newLV(self, vg, name=None, fmt_type=None, size=None): + """ Return a new LVMLogicalVolumeDevice instance. """ + if name and name in [d.name for d in self.devices]: + raise ValueError("name already in use") + elif not name: + name = self.createSuggestedLVName() + return LVMLogicalVolumeDevice(name, + format=getFormat(fmt_type), + size=size) + + def createDevice(self, device): + """ Schedule creation of a device. + + TODO: We could do some things here like assign the next + available raid minor if one isn't already set. + """ + action = ActionCreateDevice(device) + self.devicetree.registerAction(action) + + def deleteDevice(self, device): + """ Schedule deletion of a device. """ + action = ActionDestroyDevice(device) + self.devicetree.registerAction(action) + + def formatDevice(self, device, format): + """ Schedule formatting of a device. """ + self.devicetree.registerAction(ActionDestroyFormat(device)) + self.devicetree.registerAction(ActionCreateFormat(device, format)) + + def formatByDefault(self, device): + """Return whether the device should be reformatted by default.""" + formatlist = ['/boot', '/var', '/tmp', '/usr'] + exceptlist = ['/home', '/usr/local', '/opt', '/var/www'] + + if not device.format.linuxNative: + return False + + if device.format.mountable: + if device.format.mountpoint == "/" or \ + device.format.mountpoint in formatlist: + return True + + for p in formatlist: + if device.format.mountpoint.startswith(p): + for q in exceptlist: + if device.format.mountpoint.startswith(q): + return False + return True + elif device.format.type == "swap": + return True + + # be safe for anything else and default to off + return False + + def extendedPartitionsSupported(self): + """ Return whether any disks support extended partitions.""" + for disk in self.disks: + if disk.partedDisk.supportsFeature(parted.DISK_TYPE_EXTENDED): + return True + return False + + def createSuggestedVGName(self, network): + """ Return a reasonable, unused VG name. """ + # try to create a volume group name incorporating the hostname + hn = network.hostname + vgnames = [vg.name for vg in self.vgs] + 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 vgtemplate not in vgnames: + return vgtemplate + else: + i = 0 + while 1: + tmpname = "%s%02d" % (vgtemplate, i,) + if not tmpname in vgnames: + break + + i += 1 + if i > 99: + tmpname = "" + + return tmpname + + def createSuggestedLVName(self, vg): + """Given list of LV requests, come up with a reasonable LV name + """ + i = 0 + # lv.name is of the form "$VGNAME-$LVNAME" so use lv.lvname instead + lnames = [lv.lvname for lv in vg.lvs] + while 1: + tmpname = "LogVol%02d" % (i,) + if tmpname not in lnames: + break + + i += 1 + if i > 99: + tmpname = "" + + return tmpname + + +def findExistingRootDevices(anaconda, upgradeany=False): + """ Return a list of all root filesystems in the device tree. """ + rootDevs = [] + + if not os.path.exists(anaconda.rootPath): + iutil.mkdirChain(anaconda.rootPath) + + roots = [] + for device in anaconda.id.storage.devicetree.leaves: + if not device.format.linuxNative or not device.format.mountable: + continue + + if device.name in anaconda.id.storage.protectedPartitions: + # can't upgrade the part holding hd: media so why look at it? + continue + + try: + device.setup() + except Exception as e: + log.warning("setup of %s failed: %s" % (device.name, e)) + continue + + try: + device.format.mount(options="ro", mountpoint=anaconda.rootPath) + except Exception as e: + log.warning("mount of %s as %s failed: %s" % (device.name, + device.format.type, + e)) + device.teardown() + continue + + if os.access(anaconda.rootPath + "/etc/fstab", os.R_OK): + relstr = getReleaseString(anaconda.rootPath) + if upgradeany or productMatches(relstr, productName): + rootDevs.append((device.path, relstr)) + + # this handles unmounting the filesystem + device.teardown(recursive=True) + + return rootDevs + +def mountExistingSystem(anaconda, rootDevice, + allowDirty=None, warnDirty=None, + readOnly=None): + """ Mount filesystems specified in rootDevice's /etc/fstab file. """ + rootPath = anaconda.rootPath + fsset = anaconda.id.storage.fsset + if readOnly: + readOnly = "ro" + else: + readOnly = "" + + if rootDevice.path in anaconda.id.storage.protectedPartitions and \ + os.path.ismount("/mnt/isodir"): + isys.mount("/mnt/isodir", + rootPath, + fstype=rootDevice.format.type, + bindMount=True) + else: + rootDevice.setup() + rootDevice.format.mount(chroot=rootPath, + mountpoint="/", + options=readOnly) + + fsset.parseFSTab(chroot=rootPath) + + # check for dirty filesystems + dirtyDevs = [] + for device in fsset.devices: + if not hasattr(device.format, "isDirty"): + continue + + try: + device.setup() + except DeviceError as e: + # we'll catch this in the main loop + continue + + if device.format.isDirty: + log.info("%s contains a dirty %s filesystem" % (device.path, + device.format.type)) + dirtyDevs.append(device.path) + + messageWindow = anaconda.intf.messageWindow + if not allowDirty and dirtyDevs: + messageWindow(_("Dirty File Systems"), + _("The following file systems for your Linux system " + "were not unmounted cleanly. Please boot your " + "Linux installation, let the file systems be " + "checked and shut down cleanly to upgrade.\n" + "%s") % "\n".join(dirtyDevs)) + anaconda.id.storage.devicetree.teardownAll() + sys.exit(0) + elif warnDirty and dirtyDevs: + rc = messageWindow(_("Dirty File Systems"), + _("The following file systems for your Linux " + "system were not unmounted cleanly. Would " + "you like to mount them anyway?\n" + "%s") % "\n".join(dirtyDevs), + type = "yesno") + if rc == 0: + return -1 + + if flags.setupFilesystems: + fsset.mountFilesystems(anaconda, readOnly=readOnly, skipRoot=True) + + +class BlkidTab(object): + """ Dictionary-like interface to blkid.tab with device path keys """ + def __init__(self, chroot=""): + self.chroot = chroot + self.devices = {} + + def parse(self): + path = "%s/etc/blkid/blkid.tab" % self.chroot + log.debug("parsing %s" % path) + with open(path) as f: + for line in f.readlines(): + # this is pretty ugly, but an XML parser is more work than + # is justifiable for this purpose + if not line.startswith("\n")] + (data, sep, device) = line.partition(">") + if not device: + continue + + self.devices[device] = {} + for pair in data.split(): + try: + (key, value) = pair.split("=") + except ValueError: + continue + + self.devices[device][key] = value[1:-1] # strip off quotes + + def __getitem__(self, key): + return self.devices[key] + + def get(self, key, default=None): + return self.devices.get(key, default) + + +class CryptTab(object): + """ Dictionary-like interface to crypttab entries with map name keys """ + def __init__(self, devicetree, blkidTab=None): + self.devicetree = devicetree + self.blkidTab = blkidTab + self.chroot = chroot + self.mappings = {} + + def parse(self, chroot=""): + """ Parse /etc/crypttab from an existing installation. """ + if not chroot or not os.path.isdir(chroot): + chroot = "" + + path = "%s/etc/crypttab" % chroot + log.debug("parsing %s" % path) + with open(path) as f: + for line in f.readlines(): + if not self.blkidTab: + try: + self.blkidTab = BlkidTab(chroot=chroot) + self.blkidTab.parse() + except Exception: + self.blkidTab = None + + for line in lines: + (line, pound, comment) = line.partition("#") + fields = line.split() + if not 2 <= len(fields) <= 4: + continue + elif len(fields) == 2: + fields.extend(['none', '']) + elif len(fields) == 3: + fields.append('') + + (name, devspec, keyfile, options) = fields + + # resolve devspec to a device in the tree + device = resolveDevice(self.devicetree, + devspec, + blkidTab=self.blkidTab) + if device: + self.mappings[name] = {"device": device, + "keyfile": keyfile, + "options": options} + + def populate(self): + """ Populate the instance based on the device tree's contents. """ + for device in self.devicetree: + # XXX should we put them all in there or just the ones that + # are part of a device containing swap or a filesystem? + # + # Put them all in here -- we can filter from FSSet + if device.format.type != "luks": + continue + + key_file = device.format.keyFile + if not key_file: + key_file = "none" + + options = device.format.options + if not options: + options = "" + + self.mappings[device.format.mapName] = {"device": device, + "keyfile": key_file, + "options": options} + + def crypttab(self): + """ Write out /etc/crypttab """ + crypttab = "" + for name in self.mappings: + entry = self[name] + crypttab += "%s UUID=%s %s %s" % (name, + entry['device'].format.uuid, + entry['keyfile'], + entry['options']) + return crypttab + + def __getitem__(self, key): + return self.mappings[key] + + def get(self, key, default=None): + return self.mappings.get(key, default) + +def get_containing_device(path, devicetree): + """ Return the device that a path resides on. """ + if not os.path.exists(path): + return None + + st = os.stat(path) + major = os.major(st.st_dev) + minor = os.minor(st.st_dev) + link = "/sys/dev/block/%s:%s" % (major, minor) + if not os.path.exists(link): + return None + + try: + device_name = os.path.basename(os.readlink(link)) + except Exception: + return None + + return devicetree.getDeviceByName(device_name) + + +class FSSet(object): + """ A class to represent a set of filesystems. """ + def __init__(self, devicetree): + self.devicetree = devicetree + self.cryptTab = None + self.blkidTab = None + + @property + def devices(self): + devices = self.devicetree.devices.values() + devices.sort(key=lambda d: d.path) + return devices + + def parseFSTab(self, chroot=""): + """ parse /etc/fstab + + preconditions: + all storage devices have been scanned, including filesystems + postconditions: + + FIXME: control which exceptions we raise + + XXX do we care about bind mounts? + how about nodev mounts? + loop mounts? + """ + if not chroot or not os.path.isdir(chroot): + chroot = "" + + path = "%s/etc/fstab" % chroot + if not os.access(path, os.R_OK): + # XXX should we raise an exception instead? + log.info("cannot open %s for read" % path) + return + + blkidTab = BlkidTab(chroot=chroot) + try: + blkidTab.parse() + log.debug("blkid.tab devs: %s" % blkidTab.devices.keys()) + except Exception as e: + log.info("error parsing blkid.tab: %s" % e) + blkidTab = None + + cryptTab = CryptTab(self.devicetree, blkidTab=blkidTab) + try: + cryptTab.parse(chroot=chroot) + log.debug("crypttab maps: %s" % cryptTab.mappings.keys()) + except Exception as e: + log.info("error parsing crypttab: %s" % e) + cryptTab = None + + self.blkidTab = blkidTab + self.cryptTab = cryptTab + + with open(path) as f: + log.debug("parsing %s" % path) + + lines = f.readlines() + + # save the original file + self.origFStab = ''.join(lines) + + for line in lines: + # strip off comments + (line, pound, comment) = line.partition("#") + fields = line.split() + + if not 4 <= len(fields) <= 6: + continue + elif len(fields) == 4: + fields.extend([0, 0]) + elif len(fields) == 5: + fields.append(0) + + (devspec, mountpoint, fstype, options, dump, passno) = fields + + # find device in the tree + device = resolveDevice(self.devicetree, + devspec, + cryptTab=cryptTab, + blkidTab=blkidTab) + if device: + # fall through to the bottom of this block + pass + elif devspec.startswith("/dev/loop"): + # FIXME: create devices.LoopDevice + log.warning("completely ignoring your loop mount") + elif ":" in devspec: + # NFS -- preserve but otherwise ignore + device = NFSDevice(devspec, + format=getFormat(fstype, + device=devspec), + exists=True) + elif devspec.startswith("/") and fstype == "swap": + # swap file + device = FileDevice(devspec, + parents=get_containing_device(devspec), + format=getFormat(fstype, + device=devspec, + exists=True) + exists=True) + elif fstype == "bind" or "bind" in options: + # bind mount... set fstype so later comparison won't + # turn up false positives + fstype = "bind" + device = FileDevice(devspec, + parents=get_containing_device(devspec), + exists=True) + device.format = getFormat("bind", + device=device.path, + exists=True) + else: + # nodev filesystem -- preserve or drop completely? + format = getFormat(fstype) + if isinstance(format, NoDevFS): + device = NoDevice(fs) + else: + device = Device(devspec) + + if device is None: + log.error("failed to resolve %s (%s) from fstab" % (devspec, + fstype)) + continue + + # make sure, if we're using a device from the tree, that + # the device's format we found matches what's in the fstab + fmt = getFormat(fstype, device=device.path) + if fmt.type != device.format.type: + log.warning("scanned format (%s) differs from fstab " + "format (%s)" % (device.format.type, fstype)) + + if device.format.mountable: + device.format.mountpoint = mountpoint + device.format.mountopts = options + + # is this useful? + try: + device.format.options = options + except AttributeError: + pass + + if device not in self.devicetree.devices.values(): + self.devicetree._addDevice(device) + + def fsFreeSpace(self, chroot='/'): + space = [] + for device in self.devices: + if not device.format.mountable or \ + not device.format.status: + continue + + path = "%s/%s" % (chroot, device.format.mountpoint) + try: + space.append((device.format.mountpoint, + isys.pathSpaceAvailable(path))) + except SystemError: + log.error("failed to calculate free space for %s" % (device.format.mountpoint,)) + + space.sort(key=lambda s: s[1]) + return space + + def mtab(self): + format = "%s %s %s %s 0 0\n" + mtab = "" + for device in self.devices: + if not device.format.status: + continue + if not device.format.mountable: + continue + if device.format.mountpoint: + options = device.format.mountopts + options = options.replace("defaults,", "") + options = options.replace("defaults", "") + if options: + options = "rw," + options + else: + options = "rw" + mtab = mtab + format % (device.path, + device.format.mountpoint, + device.format.type, + options) + return mtab + + def turnOnSwap(self, intf=None, upgrading=None): + for device in self.swapDevices: + try: + device.setup() + device.setupFormat() + except SuspendError: + if intf: + if upgrading: + msg = _("The swap device:\n\n %s\n\n" + "in your /etc/fstab file is currently in " + "use as a software suspend device, " + "which means your system is hibernating. " + "To perform an upgrade, please shut down " + "your system rather than hibernating it.") \ + % device.path) + else: + msg = _("The swap device:\n\n %s\n\n" + "in your /etc/fstab file is currently in " + "use as a software suspend device, " + "which means your system is hibernating. " + "If you are performing a new install, " + "make sure the installer is set " + "to format all swap devices.") \ + % device.path) + + intf.messageWindow(_("Error"), msg) + sys.exit(0) + except DeviceError as msg: + if intf: + if upgrading: + err = _("Error enabling swap device %s: %s\n\n" + "The /etc/fstab on your upgrade partition " + "does not reference a valid swap " + "device.\n\nPress OK to exit the " + "installer") % (device.path, msg) + else: + err = _("Error enabling swap device %s: %s\n\n" + "This most likely means this swap " + "device has not been initialized.\n\n" + "Press OK to exit the installer.") % \ + (device.path, msg) + intf.messageWindow(_("Error"), err) + sys.exit(0) + + def mountFilesystems(self, anaconda, raiseErrors=None, readOnly=None, + skipRoot=False): + intf = anaconda.intf + for device in [d for d in self.devices if d.isleaf]: + if not device.format.mountable: + continue + + if skipRoot and mountpoint == "/": + continue + + if "noauto" in options.split(","): + continue + + try: + device.setup() + except Exception as msg: + # FIXME: need an error popup + continue + + if readOnly: + options = "%s,%s" % (options, readOnly) + + try: + device.format.mount(options=options, chroot=anaconda.rootPath) + except OSError as (num, msg): + if intf: + if num == errno.EEXIST: + intf.messageWindow(_("Invalid mount point"), + _("An error occurred when trying " + "to create %s. Some element of " + "this path is not a directory. " + "This is a fatal error and the " + "install cannot continue.\n\n" + "Press to exit the " + "installer.") % (mountpoint,)) + else: + intf.messageWindow(_("Invalid mount point"), + _("An error occurred when trying " + "to create %s: %s. This is " + "a fatal error and the install " + "cannot continue.\n\n" + "Press to exit the " + "installer.") % (mountpoint, + msg)) + log.error("OSError: (%d) %s" % (num, msg) ) + sys.exit(0) + except SystemError as (num, msg): + if raiseErrors: + raise + if intf and not device.format.linuxNative: + ret = intf.messageWindow(_("Unable to mount filesystem"), + _("An error occurred mounting " + "device %s as %s. You may " + "continue installation, but " + "there may be problems.") % + (device.path, + mountpoint), + type="custom", + custom_icon="warning", + custom_buttons=[_("_Exit installer"), + _("_Continue")]) + + if ret == 0: + sys.exit(0) + else: + continue + + log.error("SystemError: (%d) %s" % (num, msg) ) + sys.exit(0) + except FSError as msg: + if intf: + intf.messageWindow(_("Unable to mount filesystem"), + _("An error occurred mounting " + "device %s as %s: %s. This is " + "a fatal error and the install " + "cannot continue.\n\n" + "Press to exit the " + "installer.") % (mountpoint, + msg)) + log.error("FSError: %s" % msg) + sys.exit(0) + + def umountFilesystems(self, instPath, ignoreErrors=True, swapoff=True): + # XXX if we tracked the /dev bind mount this wouln't be necessary + if os.path.ismount("%s/dev" % instPath): + isys.umount("%s/dev" % instPath, removeDir=0) + + # reverse works in place so we take a slice + devices = self.devices[:] + devices.reverse() + for device in [d for d in devices if d.isleaf]: + if not device.format.mountable and \ + (device.format.type != "swap" or swapoff): + continue + + device.teardownFormat() + device.teardown() + + def createSwapFile(self, device, rootPath, size): + """ Create and activate a swap file under rootPath. """ + filename = "/SWAP" + count = 0 + while os.path.exists("%s/%s" % (rootPath, filename) or \ + self.devicetree.getDeviceByName(filename): + file = os.path.normpath("%s/%s" % (rootPath, filename)) + count += 1 + filename = "/SWAP-%d" % count + + dev = FileDevice(filename, + size=size, + parents=self.rootDevice, + format=getFormat("swap", device=filename)) + dev.create() + dev.createFormat() + dev.setup() + dev.setupFormat() + # nasty, nasty + self.devicetree._addDevice(dev) + + def mkDevRoot(self, instPath): + root = self.rootDevice + dev = "%s/dev/%s" % (instPath, root.device.getDevice()) + if not os.path.exists("%s/dev/root" %(instPath,)) and os.path.exists(dev): + rdev = os.stat(dev).st_rdev + os.mknod("%s/dev/root" % (instPath,), stat.S_IFBLK | 0600, rdev) + + @property + def swapDevices(self): + swaps = [] + for device in self.devices: + if device.format.type == "swap": + swaps.append(device) + return swaps + + @property + def rootDevice(self): + for device in self.devices: + try: + mountpoint = device.format.mountpoint + except AttributeError: + mountpoint = None + + if mountpoint == "/": + return device + + @property + def migratableDevices(self): + """ Return a list of devices whose filesystems can be migrated. """ + return [] # no migrate for you!!! + + def write(self, chroot="/"): + """ write out all config files based on the set of filesystems """ + pass + + def crypttab(self): + # if we are upgrading, do we want to update crypttab? + # gut reaction says no, but plymouth needs the names to be very + # specific for passphrase prompting + if not self.cryptTab: + self.cryptTab = CryptTab(self.devicetree) + self.cryptTab.populate() + + # prune crypttab -- only mappings required by one or more entries + for name in self.cryptTab.mappings: + keep = False + mapInfo = self.cryptTab[name] + cryptoDev = mapInfo['device'] + for device in self.devices: + if device.dependsOn(cryptoDev): + keep = True + break + + if not keep: + del self.cryptTab.mappings[name] + + return self.cryptTab.crypttab() + + def mdadmConf(self): + """ Return the contents of mdadm.conf. """ + arrays = self.devicetree.getDevicesByType("mdarray") + conf = "" + for array in arrays: + writeConf = False + if array in self.devices: + writeConf = True + else: + for device in self.devices: + if device.dependsOn(array): + writeConf = True + break + + if writeConf: + conf += array.mdadmConfEntry + + return conf + + def writeFSTab(self, chroot="/"): + """ Write out /etc/fstab. """ + pass + + def fstab (self): + format = "%-23s %-23s %-7s %-15s %d %d\n" + fstab = """ +# +# /etc/fstab +# Created by anaconda on %s +# +# Accessible filesystems, by reference, are maintained under '/dev/disk' +# See man pages fstab(5), findfs(8), mount(8) and/or vol_id(8) for more info +# +""" % time.asctime() + + for device in self.device: + # why the hell do we put swap in the fstab, anyway? + if not device.format.mountable and device.format.type != "swap": + continue + + fstype = device.format.type + if fstype == "swap": + mountpoint = "swap" + options = device.format.options + else: + mountpoint = device.format.mountpoint + options = gettattr(device.format.mountopts + if not mountpoint: + log.warning("%s filesystem on %s has no mountpoint" % \ + (fstype, + device.path)) + continue + + if not options: + # why not, right? + options = "defaults" + + devspec = device.fstabSpec + dump = device.format.dump + if device.format.check and mountpoint == "/": + passno = 1 + elif device.format.check: + passno = 2 + else: + passno = 0 + fstab = fstab + device.fstabComment + fstab = fstab + format % (devspec, mountpoint, fstype, + options, dump, passno) + return fstab + + @property + def bootDevice(self): + mntDict = {} + bootDev = None + for device in [d for d in self.devices if d.format.mountable]: + mntDict[device.format.mountpoint] = device + + # FIXME: this ppc stuff feels kind of crufty -- the abstraction + # here needs a little bit of work + if iutil.getPPCMacGen() == "NewWorld": + for device in self.devicetree.devices.values(): + # XXX do we need to also check the size? + if device.format.type == "hfs" and device.bootable: + bootDev = device + elif (iutil.getPPCMachine() == "pSeries" or + iutil.getPPCMachine() == "iSeries"): + # we want the first prep partition or the first newly formatted one + # XXX who cares if PReP is formatted? it has no format. + partitions = self.devicetree.getDeviceByType("partition") + for device in partitions: + if device.partedPartition.getFlag(parted.PARTITION_PREP) and \ + (bestprep is None or \ + (bestprep.format.exists and not device.format.exists)): + bootDev = device + elif iutil.isEfi(): + bootDev = mntDict.get('/boot/efi') + else: + bootDev = mntDict.get("/boot", mntDict.get("/")) + + # XXX should we raise an error if no boot device was found? + return bootDev + + def bootloaderChoices(self, bl): + ret = {} + bootDev = self.bootDevice + + if bootDev is None: + log.warning("no boot device set") + return ret + + if iutil.isEfi(): + ret['boot'] = (bootDev.path, N_("EFI System Partition")) + elif bootDev.device.getName() == "RAIDDevice": + ret['boot'] = (bootDev.path, N_("RAID Device")) + ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) + elif iutil.getPPCMacGen() == "NewWorld": + ret['boot'] = (bootDev.path, N_("Apple Bootstrap")) + partitions = self.devicetree.getDevicesByType("partition") + for (n, device) in enumerate(partitions): + if (device.format.type == "hfs" and device.bootable and \ + device.path != bootDev.path)): + ret['boot%d' %(n,)] = (device.path, + N_("Apple Bootstrap")) + elif (iutil.getPPCMachine() == "pSeries" or + iutil.getPPCMachine() == "iSeries"): + ret['boot'] = (bootDev.device, N_("PPC PReP Boot")) + else: + ret['boot'] = (bootDev.device, + N_("First sector of boot partition")) + ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) + + return ret + + +def resolveDevice(tree, devspec, blkidTab=None, cryptTab=None): + # find device in the tree + device = None + if devspec.startswith("UUID="): + # device-by-uuid + uuid = devspec.partition("=")[2] + device = tree.uuids.get(uuid) + if device is None: + log.error("failed to resolve device %s" % devspec) + elif devspec.startswith("LABEL="): + # device-by-label + label = devspec.partition("=")[2] + device = tree.fslabels.get(label) + if device is None: + log.error("failed to resolve device %s" % devspec) + elif devspec.startswith("/dev/"): + # device path + device = tree.devices.get(devspec) + if device is None: + if blkidTab: + # try to use the blkid.tab to correlate the device + # path with a UUID + blkidTabEnt = blkidTab.get(devspec) + if blkidTabEnt: + log.debug("found blkid.tab entry for '%s'" % devspec) + uuid = blkidTabEnt.get("UUID") + if uuid: + device = tree.getDeviceByUuid(uuid) + if device: + devstr = device.name + else: + devstr = "None" + log.debug("found device '%s' in tree" % devstr) + if device and device.format and \ + device.format.type == "luks": + map_name = device.format.mapName + log.debug("luks device; map name is '%s'" % map_name) + mapped_dev = tree.getDeviceByName(map_name) + if mapped_dev: + device = mapped_dev + + if device is None and cryptTab and \ + devspec.startswith("/dev/mapper/"): + # try to use a dm-crypt mapping name to + # obtain the underlying device, possibly + # using blkid.tab + cryptTabEnt = cryptTab.get(devspec.split("/")[-1]) + if cryptTabEnt: + luks_dev = cryptTabEnt['device'] + try: + device = tree.getChildren(luks_dev)[0] + except IndexError as e: + pass + elif device is None: + # dear lvm: can we please have a few more device nodes + # for each logical volume? + # three just doesn't seem like enough. + name = devspec[5:] # strip off leading "/dev/" + (vg_name, slash, lv_name) = name.partition("/") + if lv_name and not "/" in lv_name: + # looks like we may have one + lv = "%s-%s" % (vg_name, lv_name) + device = tree.getDeviceByName(lv) + + if device: + log.debug("resolved '%s' to '%s' (%s)" % (devspec, device.name, device.type)) + else: + log.debug("failed to resolve '%s'" % devspec) + return device + + + diff --git a/storage/deviceaction.py b/storage/deviceaction.py new file mode 100644 index 000000000..09c2277de --- /dev/null +++ b/storage/deviceaction.py @@ -0,0 +1,300 @@ +# deviceaction.py +# Device modification action classes for anaconda's storage configuration +# module. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# +""" action.py + +""" +from devices import StorageDevice +from errors import * + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("storage") + + +""" The values are just hints as to the ordering. + + Eg: fsmod and devmod ordering depends on the mod (shrink -v- grow) +""" +ACTION_TYPE_NONE = 0 +ACTION_TYPE_DESTROY = 1000 +ACTION_TYPE_RESIZE = 500 +ACTION_TYPE_MIGRATE = 250 +ACTION_TYPE_CREATE = 100 + +action_strings = {ACTION_TYPE_NONE: "None", + ACTION_TYPE_DESTROY: "Destroy", + ACTION_TYPE_RESIZE: "Resize", + ACTION_TYPE_MIGRATE: "Migrate", + ACTION_TYPE_CREATE: "Create"} + +ACTION_OBJECT_NONE = 0 +ACTION_OBJECT_FORMAT = 1 +ACTION_OBJECT_DEVICE = 2 + +object_strings = {ACTION_OBJECT_NONE: "None", + ACTION_OBJECT_FORMAT: "Format", + ACTION_OBJECT_DEVICE: "Device"} + +RESIZE_SHRINK = 88 +RESIZE_GROW = 89 + +resize_strings = {RESIZE_SHRINK: "Shrink", + RESIZE_GROW: "Grow"} + +class DeviceAction(object): + """ An action that will be carried out in the future on a Device. + + These classes represent actions to be performed on devices or + filesystems. + + The operand Device instance will be modified according to the + action, but no changes will be made to the underlying device or + filesystem until the DeviceAction instance's execute method is + called. The DeviceAction instance's cancel method should reverse + any modifications made to the Device instance's attributes. + + If the Device instance represents a pre-existing device, the + constructor should call any methods or set any attributes that the + action will eventually change. Device/DeviceFormat classes should verify + that the requested modifications are reasonable and raise an + exception if not. + + Only one action of any given type/object pair can exist for any + given device at any given time. This is enforced by the + DeviceTree. + + Basic usage: + + a = DeviceAction(dev) + a.execute() + + OR + + a = DeviceAction(dev) + a.cancel() + + + XXX should we back up the device with a deep copy for forcibly + cancelling actions? + + The downside is that we lose any checking or verification that + would get done when resetting the Device instance's attributes to + their original values. + + The upside is that we would be guaranteed to achieve a total + reversal. No chance of, eg: resizes ending up altering Device + size due to rounding or other miscalculation. +""" + type = ACTION_TYPE_NONE + obj = ACTION_OBJECT_NONE + + def __init__(self, device): + if not isinstance(device, StorageDevice): + raise ValueError("arg 1 must be a StorageDevice instance") + self.device = device + #self._backup = deepcopy(device) + + def execute(self): + """ perform the action """ + pass + + def cancel(self): + """ cancel the action """ + pass + + def isDestroy(self): + return self.type == ACTION_TYPE_DESTROY + + def isCreate(self): + return self.type == ACTION_TYPE_CREATE + + def isMigrate(self): + return self.type == ACTION_TYPE_MIGRATE + + def isResize(self): + return self.type == ACTION_TYPE_RESIZE + + def isShrink(self): + return (self.type == ACTION_TYPE_RESIZE and self.dir == RESIZE_SHRINK) + + def isGrow(self): + return (self.type == ACTION_TYPE_RESIZE and self.dir == RESIZE_GROW) + + def isDevice(self): + return self.obj == ACTION_OBJECT_DEVICE + + def isFormat(self): + return self.obj == ACTION_OBJECT_FORMAT + + def __str__(self): + s = "%s %s" % (action_strings[self.type], object_strings[self.obj]) + if self.isResize(): + s += " (%s)" % resize_strings[self.dir] + if self.isFormat(): + if self.format: + fmt_type = self.format.type + else: + fmt_type = None + s += " %s on" % fmt_type + if self.isMigrate(): + pass + s += " %s (%s)" % (self.device.name, self.device.type) + return s + +class ActionCreateDevice(DeviceAction): + """ Action representing the creation of a new device. """ + type = ACTION_TYPE_CREATE + obj = ACTION_OBJECT_DEVICE + + def __init__(self, device): + # FIXME: assert device.fs is None + DeviceAction.__init__(self, device) + + def execute(self): + self.device.create() + + +class ActionDestroyDevice(DeviceAction): + """ An action representing the deletion of an existing device. """ + type = ACTION_TYPE_DESTROY + obj = ACTION_OBJECT_DEVICE + + def __init__(self, device): + # XXX should we insist that device.fs be None? + DeviceAction.__init__(self, device) + + def execute(self): + self.device.destroy() + + +class ActionResizeDevice(DeviceAction): + """ An action representing the resizing of an existing device. """ + type = ACTION_TYPE_RESIZE + obj = ACTION_OBJECT_DEVICE + + def __init__(self, device, newsize): + if device.size == newsize: + raise ValueError("new size same as old size") + + DeviceAction.__init__(self, device) + if newsize > device.size: + self.dir = RESIZE_GROW + else: + self.dir = RESIZE_SHRINK + self.origsize = device.size + self.device.size = newsize + + def execute(self): + self.device.resize() + + def cancel(self): + self.device.size = self.origsize + + +class ActionCreateFormat(DeviceAction): + """ An action representing creation of a new filesystem. """ + type = ACTION_TYPE_CREATE + obj = ACTION_OBJECT_FORMAT + + def __init__(self, device, format): + DeviceAction.__init__(self, device) + # should we insist that device.format be None? + # (indicative of ActionDestroyFormat having been instantiated) + self.origFormat = device.format + self.device.teardownFormat() + self.device.format = format + + def execute(self): + # XXX we should set partition type flag as needed + # - or should that be in the CreateDevice action? + self.device.setup() + self.device.format.create(options=self.device.formatArgs) + + def cancel(self): + self.device.format = self.origFormat + + @property + def format(self): + return self.device.format + + +class ActionDestroyFormat(DeviceAction): + """ An action representing the removal of an existing filesystem. + + XXX this seems unnecessary + """ + type = ACTION_TYPE_DESTROY + obj = ACTION_OBJECT_FORMAT + + def __init__(self, device): + DeviceAction.__init__(self, device) + self.origFormat = device.format + if device.format and device.format.status: + device.format.teardown() + self.device.format = None + + def execute(self): + """ wipe the filesystem signature from the device """ + #self.device.destroyFormat() + if self.origFormat: + self.device.setup() + self.origFormat.destroy() + + def cancel(self): + self.device.format = self.origFormat + + @property + def format(self): + return self.origFormat + + +class ActionResizeFormat(DeviceAction): + """ An action representing the resizing of an existing filesystem. + + XXX Do we even want to support resizing of a filesystem without + also resizing the device it resides on? + """ + type = ACTION_TYPE_RESIZE + obj = ACTION_OBJECT_FORMAT + + def __init__(self, device, newsize): + if device.size == newsize: + raise ValueError("new size same as old size") + + DeviceAction.__init__(self, device) + if newsize > device.format.size: + self.dir = RESIZE_GROW + else: + self.dir = RESIZE_SHRINK + self.device.format.setSize(newsize) + + def execute(self): + self.device.format.resize() + + def cancel(self): + self.device.format.setSize(None) + + + diff --git a/storage/devicelibs/__init__.py b/storage/devicelibs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/storage/devicelibs/crypto.py b/storage/devicelibs/crypto.py new file mode 100644 index 000000000..c95b82493 --- /dev/null +++ b/storage/devicelibs/crypto.py @@ -0,0 +1,195 @@ +# +# crypto.py +# +# Copyright (C) 2009 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Author(s): Dave Lehman +# + +import os + +import iutil +from errors import * + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +def is_luks(device): + rc = iutil.execWithRedirect("cryptsetup", + ["isLuks", device], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath = 1) + if rc: + return False + else: + return True + +def luks_uuid(device): + uuid = iutil.execWithCapture("cryptsetup", + ["luksUUID", device], + stderr="/dev/null") + return uuid.strip() + +def luks_status(name): + """0 means active, 1 means inactive (or non-existent)""" + rc = iutil.execWithRedirect("cryptsetup", + ["status", name], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath = 1) + return rc + +def luks_format(device, + passphrase=None, key_file=None, + cipher=None, key_size=None): + p = os.pipe() + argv = ["-q"] + os.close(p[1]) + + if cipher: + argv.extend(["--cipher", cipher]) + + if key_size: + argv.append("--key-size=%d" % key_size) + + argv.extend(["luksFormat", device]) + + if passphrase: + os.write(p[1], "%s\n" % passphrase) + elif key_file and os.path.isfile(key_file): + argv.append(key_file) + else: + raise ValueError("luks_format requires either a passphrase or a key file") + + rc = iutil.execWithRedirect("cryptsetup", + argv, + stdin = p[0], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath = 1) + + os.close(p[0]) + if rc: + raise CryptoError("luks_format failed for '%s'" % device) + +def luks_open(device, name, passphrase=None, key_file=None): + p = os.pipe() + if passphrase: + os.write(p[1], "%s\n" % passphrase) + argv = ["luksOpen", device, name] + elif key_file and os.path.isfile(key_file): + argv = ["luksOpen", "--key-file", key_file, device, name] + else: + raise ValueError("luks_open requires either a passphrase or a key file") + + os.close(p[1]) + rc = iutil.execWithRedirect("cryptsetup", + argv, + stdin = p[0], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath = 1) + + os.close(p[0]) + if rc: + raise CryptoError("luks_open failed for %s (%s)" % (device, name)) + +def luks_close(name): + rc = iutil.execWithRedirect("cryptsetup", + ["luksClose", name], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath = 1) + + if rc: + raise CryptoError("luks_close failed for %s" % name) + +def luks_add_key(device, + new_passphrase=None, new_key_file=None, + passphrase=None, key_file=None): + p = os.pipe() + if passphrase: + os.write(p[1], "%s\n" % passphrase) + key_spec = "" + elif key_file and os.path.isfile(key_file): + key_spec = "--key-file %s" % key_file + else: + raise ValueError("luks_add_key requires either a passphrase or a key file") + + if new_passphrase: + os.write(p[1], "%s\n" % new_passphrase) + new_key_spec = "" + elif new_key_file and os.path.isfile(new_key_file): + new_key_spec = "%s" % new_key_file + else: + raise ValueError("luks_add_key requires either a passphrase or a key file to add") + + os.close(p[1]) + + rc = iutil.execWithRedirect("cryptsetup", + ["-q", + key_spec, + "luksAddKey", + device, + new_key_spec], + stdin = p[0], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath = 1) + + os.close(p[0]) + if rc: + raise CryptoError("luks add key failed") + +def luks_remove_key(device, + del_passphrase=None, del_key_file=None, + passphrase=None, key_file=None): + p = os.pipe() + if passphrase: + os.write(p[1], "%s\n" % passphrase) + key_spec = "" + elif key_file and os.path.isfile(key_file): + key_spec = "--key-file %s" % key_file + else: + raise ValueError("luks_remove_key requires either a passphrase or a key file") + + if del_passphrase: + os.write(p[1], "%s\n" % del_passphrase) + del_key_spec = "" + elif del_key_file and os.path.isfile(del_key_file): + del_key_spec = "%s" % del_key_file + else: + raise ValueError("luks_remove_key requires either a passphrase or a key file to remove") + + os.close(p[1]) + + rc = iutil.execWithRedirect("cryptsetup", + ["-q", + key_spec, + "luksRemoveKey", + device, + del_key_spec], + stdin = p[0], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath = 1) + + os.close(p[0]) + if rc: + raise CryptoError("luks_remove_key failed") + + diff --git a/storage/devicelibs/dm.py b/storage/devicelibs/dm.py new file mode 100644 index 000000000..adf8cee7f --- /dev/null +++ b/storage/devicelibs/dm.py @@ -0,0 +1,77 @@ + +import os + +import iutil +from errors import * + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("storage") + +def name_from_dm_node(dm_node): + st = os.stat("/dev/%s" % dm_node) + major = os.major(st.st_rdev) + minor = os.minor(st.st_rdev) + name = iutil.execWithCapture("dmsetup", + ["info", "--columns", + "--noheadings", "-o", "name", + "-j", str(major), "-m", str(minor)], + stderr="/dev/null") + log.debug("name_from_dm(%s) returning '%s'" % (dm_node, name.strip())) + return name.strip() + +def dm_node_from_name(map_name): + devnum = iutil.execWithCapture("dmsetup", + ["info", "--columns", + "--noheadings", + "-o", "devno", + map_name], + stderr="/dev/null") + (major, sep, minor) = devnum.strip().partition(":") + if not sep: + raise DMError("dm device does not exist") + + dm_node = "dm-%d" % int(minor) + log.debug("dm_node_from_name(%s) returning '%s'" % (map_name, dm_node)) + return dm_node + +def _get_backing_devnums_from_map(map_name): + ret = [] + buf = iutil.execWithCapture("dmsetup", + ["info", "--columns", + "--noheadings", + "-o", "devnos_used", + map_name], + stderr="/dev/null") + dev_nums = buf.split() + for dev_num in dev_nums: + (major, colon, minor) = dev_num.partition(":") + ret.append((int(major), int(minor))) + + return ret + +def get_backing_devnums(dm_node): + #dm_node = dm_node_from_name(map_name) + if not dm_node: + return None + + top_dir = "/sys/block" + backing_devs = os.listdir("%s/%s/slaves/" % (top_dir, dm_node)) + dev_nums = [] + for backing_dev in backing_devs: + dev_num = open("%s/%s/dev" % (top_dir, backing_dev)).read().strip() + (_major, _minor) = dev_num.split(":") + dev_nums.append((int(_major), int(_minor))) + + return dev_nums + +def get_backing_devs_from_name(map_name): + dm_node = dm_node_from_name(map_name) + if not dm_node: + return None + + slave_devs = os.listdir("/sys/block/virtual/%s" % dm_node) + return slave_devs + diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py new file mode 100644 index 000000000..af1a9b7e5 --- /dev/null +++ b/storage/devicelibs/lvm.py @@ -0,0 +1,289 @@ + +import os +import math + +import iutil + +from errors import * + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +def has_lvm(): + has_lvm = False + for path in os.environ["PATH"].split(":"): + if os.access("%s/lvm" % path, os.X_OK): + has_lvm = True + break + + if has_lvm: + has_lvm = False + for line in open("/proc/devices").readlines(): + if "device-mapper" in line.split(): + has_lvm = True + break + + return has_lvm + +def getPossiblePhysicalExtents(floor=0): + """Returns a list of integers representing the possible values for + the physical extent of a volume group. Value is in KB. + + floor - size (in KB) of smallest PE we care about. + """ + + possiblePE = [] + curpe = 8 + while curpe <= 16384*1024: + if curpe >= floor: + possiblePE.append(curpe) + curpe = curpe * 2 + + return possiblePE + +def getMaxLVSize(pe): + """Given a PE size in KB, returns maximum size (in MB) of a logical volume. + + pe - PE size in KB + """ + if iutil.getArch() in ("x86_64", "ppc64"): #64bit architectures + return (8*1024*1024*1024*1024) #Max is 8EiB (very large number..) + else: + return (16*1024*1024) #Max is 16TiB + +def safeLvmName(str): + tmp = string.strip(str) + tmp = tmp.replace("/", "_") + tmp = re.sub("[^0-9a-zA-Z._]", "", str) + tmp = tmp.lstrip("_") + + return tmp + +def getVGUsedSpace(vgreq, requests, diskset): + vgused = 0 + for request in requests.requests: + if request.type == REQUEST_LV and request.volumeGroup == vgreq.uniqueID: + size = int(request.getActualSize(requests, diskset)) + vgused = vgused + size + + + return vgused + +def getVGFreeSpace(vgreq, requests, diskset): + raise NotImplementedError + used = getVGUsedSpace(vgreq, requests, diskset) + log.debug("used space is %s" % (used,)) + + total = vgreq.getActualSize(requests, diskset) + log.debug("actual space is %s" % (total,)) + return total - used + +def clampSize(size, pesize, roundup=None): + if roundup: + round = math.ceil + else: + round = math.floor + + return long(round(float(size)/float(pesize)) * pesize) + +def pvcreate(device): + rc = iutil.execWithRedirect("lvm", + ["pvcreate", device], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath=1) + if rc: + raise LVMError("pvcreate failed for %s" % device) + +def pvresize(device, size): + size_arg = "%dm" % size + rc = iutil.execWithRedirect("lvm", + ["pvresize", + "--setphysicalvolumesize", size_arg, + device], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath=1) + if rc: + raise LVMError("pvresize failed for %s" % device) + +def pvremove(device): + rc = iutil.execWithRedirect("lvm", + ["pvremove", device], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath=1) + if rc: + raise LVMError("pvremove failed for %s" % device) + +def pvinfo(device): + """ + If the PV was created with '--metadacopies 0', lvm will do some + scanning of devices to determine from their metadata which VG + this PV belongs to. + + pvs -o pv_name,pv_mda_count,vg_name,vg_uuid --config \ + 'devices { scan = "/dev" filter = ["a/loop0/", "r/.*/"] }' + """ + #cfg = "'devices { scan = \"/dev\" filter = [\"a/%s/\", \"r/.*/\"] }'" + rc = iutil.execWithCapture("lvm", + ["pvs", "--noheadings", + "--units", "m", + "-o", + "pv_name,pv_mda_count,vg_name,vg_uuid", + device], + stderr = "/dev/null") + vals = rc.split() + if not vals: + raise LVMError("pvinfo failed for %s" % device) + + info = {'pv_name': vals[0], + 'vg_name': vals[2], + 'vg_uuid': vals[3]} + return info + +def vgcreate(vg_name, pvs, pe_size): + argv = ["vgcreate"] + if pe_size: + argv.extend(["-s", "%dM" % pe_size]) + pv_list = " ".join(pvs) + argv.append(vg_name) + argv.append(pv_list) + rc = iutil.execWithRedirect("lvm", + argv, + stdout = "/dev/null", + stderr = "/dev/null", + searchPath=1) + + if rc: + raise LVMError("vgcreate failed for %s" % vg_name) + +def vgremove(vg_name): + rc = iutil.execWithRedirect("lvm", ["vgremove", vg_name], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath=1) + + if rc: + raise LVMError("vgremove failed for %s" % vg_name) + +def vgactivate(vg_name): + rc = iutil.execWithRedirect("lvm", ["vgchange" "-a", "y", vg_name], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath=1) + if rc: + raise LVMError("vgactivate failed for %s" % vg_name) + +def vgdeactivate(vg_name): + rc = iutil.execWithRedirect("lvm", ["vgchange", "-a", "n", vg_name], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath=1) + + if rc: + raise LVMError("vgdeactivate failed for %s" % vg_name) + +def vgreduce(vg_name, pv_list): + pvs = " ".join(pv_list) + rc = iutil.execWithRedirect("lvm", ["vgreduce", vg_name, pvs], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath=1) + + if rc: + raise LVMError("vgreduce failed for %s" % vg_name) + +def vginfo(vg_name): + buf = iutil.execWithCapture("lvm", + ["vgs", "--noheadings", "--nosuffix", "--units", "m", "-o", + "uuid,size,free,extent_size,extent_count,free_count,pv_count", + vg_name], + stderr="/dev/null") + info = buf.split() + if len(info) != 7: + raise LVMError(_("vginfo failed for %s" % vg_name)) + + d = {} + (d['uuid'],d['size'],d['free'],d['pe_size'], + d['pe_count'],d['pe_free'],d['pv_count']) = info + return d + +def lvs(vg_name): + buf = iutil.execWithCapture("lvm", + ["lvs", "--noheadings", "--nosuffix", + "--units", "m", "-o", + "lv_name,lv_uuid,lv_size"], + stderr="/dev/null") + + + lvs = {} + for line in buf.splitlines(): + line = line.strip() + if not line: + continue + (name, uuid, size) = line.split() + lvs[name] = {"size": size, + "uuid": uuid} + return lvs + +def lvcreate(vg_name, lv_name, size): + size_arg = "%dm" % size + rc = iutil.execWithRedirect("lvm", + ["lvcreate", + "-L", size_arg, + "-n", lv_name, + vg_name], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath=1) + + if rc: + raise LVMError("lvcreate failed for %s/%s" % (vg_name, lv_name)) + +def lvremove(vg_name, lv_name): + lv_path = "%s/%s" % (vg_name, lv_name) + rc = iutil.execWithRedirect("lvm", ["lvremove", lv_path], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath=1) + + if rc: + raise LVMError("lvremove failed for %s" % lv_path) + +def lvresize(vg_name, lv_name, size): + lv_path = "%s/%s" % (vg_name, lv_name) + size_arg = "%dm" % size + rc = iutil.execWithRedirect("lvm", + ["lvresize", + "-L", size_arg, + lv_path], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath=1) + + if rc: + raise LVMError("lvresize failed for %s" % lv_path) + +def lvactivate(vg_name, lv_name): + # see if lvchange accepts paths of the form 'mapper/$vg-$lv' + lv_path = "%s/%s" % (vg_name, lv_name) + rc = iutil.execWithRedirect("lvm", ["lvchange", "-a", "y", lv_path], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath=1) + if rc: + raise LVMError("lvactivate failed for %s" % lv_path) + +def lvdeactivate(vg_name, lv_name): + lv_path = "%s/%s" % (vg_name, lv_name) + rc = iutil.execWithRedirect("lvm", ["lvchange", "-a", "n", lv_path], + stdout = "/dev/null", + stderr = "/dev/null", + searchPath=1) + + if rc: + raise LVMError("lvdeactivate failed for %s" % lv_path) + + + diff --git a/storage/devicelibs/mdraid.py b/storage/devicelibs/mdraid.py new file mode 100644 index 000000000..28e0a3510 --- /dev/null +++ b/storage/devicelibs/mdraid.py @@ -0,0 +1,226 @@ + +import os + +import iutil +from errors import * + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +def getRaidLevels(): + avail = [] + try: + f = open("/proc/mdstat", "r") + except: + pass + else: + for l in f.readlines(): + if not l.startswith("Personalities"): + continue + + lst = l.split() + + for lev in ["RAID0", "RAID1", "RAID5", "RAID6", "RAID10"]: + if "[" + lev + "]" in lst or "[" + lev.lower() + "]" in lst: + avail.append(lev) + + f.close() + + avail.sort() + return avail + +raid_levels = getRaidLevels() + +# FIXME: these functions should be consolidated into one function +def isRaid10(raidlevel): + """Return whether raidlevel is a valid descriptor of RAID10.""" + return raidlevel in ("RAID10", "10", 10) + +def isRaid6(raidlevel): + """Return whether raidlevel is a valid descriptor of RAID6.""" + return raidlevel in ("RAID6", "6", 6) + +def isRaid5(raidlevel): + """Return whether raidlevel is a valid descriptor of RAID5.""" + return raidlevel in ("RAID5", "5", 5) + +def isRaid1(raidlevel): + """Return whether raidlevel is a valid descriptor of RAID1.""" + return raidlevel in ("mirror", "RAID1", "1", 1) + +def isRaid0(raidlevel): + """Return whether raidlevel is a valid descriptor of RAID0.""" + return raidlevel in ("stripe", "RAID0", "0", 0) + +def get_raid_min_members(raidlevel): + """Return the minimum number of raid members required for raid level""" + if isRaid0(raidlevel): + return 2 + elif isRaid1(raidlevel): + return 2 + elif isRaid5(raidlevel): + return 3 + elif isRaid6(raidlevel): + return 4 + elif isRaid10(raidlevel): + return 4 + else: + raise ValueError, "invalid raidlevel in get_raid_min_members" + +def get_raid_max_spares(raidlevel, nummembers): + """Return the maximum number of raid spares for raidlevel.""" + if isRaid0(raidlevel): + return 0 + elif isRaid1(raidlevel) or isRaid5(raidlevel) or \ + isRaid6(raidlevel) or isRaid10(raidlevel): + return max(0, nummembers - get_raid_min_members(raidlevel)) + else: + raise ValueError, "invalid raidlevel in get_raid_max_spares" + +def mdcreate(device, level, disks, spares=0): + argv = ["--create", device, "--level", str(level)] + raid_devs = len(disks) - spares + argv.append("--raid-devices=%d" % raid_devs) + if spares: + argv.append("--spare-devices=%d" % spares) + argv.extend(disks) + + rc = iutil.execWithRedirect("mdadm", + argv, + stderr = "/dev/null", + stdout = "/dev/null", + searchPath=1) + + if rc: + raise MDRaidError("mdcreate failed for %s" % device) + + # mdadm insists on starting the new array, so we have to stop it here + #self.mddeactivate(device) + +def mddestroy(device): + rc = iutil.execWithRedirect("mdadm", + ["--zero-superblock", device], + stderr = "/dev/null", + stdout = "/dev/null", + searchPath=1) + + if rc: + raise MDRaidError("mddestroy failed for %s" % device) + +def mdadd(device): + # XXX NOTUSED: mdadm -I is broken and dledford says it should be + # avoided if possible, so we used mdadm -A instead + rc = iutil.execWithRedirect("mdadm", + ["--incremental", + "--quiet", + "--auto=yes", + device], + stderr = "/dev/null", + stdout = "/dev/null", + searchPath=1) + + if rc: + raise MDRaidError("mdadd failed for %s" % device) + +def mdactivate(device, members=[], super_minor=None, uuid=None): + if super_minor is None and not uuid: + raise ValueError("mdactivate requires either a uuid or a super-minor") + + if uuid: + identifier = "--uuid=%s" % uuid + elif super_minor is not None: + identifier = "--super-minor=%d" % super_minor + else: + identifier = "" + + filename = None + if members: + from tempfile import mkstemp + (fd, filename) = mkstemp(prefix="%s_devices." % device, + dir="/tmp", + text=True) + os.write(fd, "DEVICE %s\n" % " ".join(members)) + config_arg = "--config=%s" % filename + os.close(fd) + del mkstemp + else: + config_arg = "" + + rc = iutil.execWithRedirect("mdadm", + ["--assemble", + config_arg, + device, + identifier, + "--auto=md", + "--update=super-minor"], + stderr = "/dev/null", + stdout = "/dev/null", + searchPath=1) + + if filename and os.access(filename, os.R_OK): + try: + os.unlink(filename) + except OSError, e: + log.debug("unlink of %s failed: %s" % (filename, e)) + + if rc: + raise MDRaidError("mdactivate failed for %s" % device) + + +def mddeactivate(device): + rc = iutil.execWithRedirect("mdadm", + ["--stop", device], + stderr = "/dev/null", + stdout = "/dev/null", + searchPath=1) + + if rc: + raise MDRaidError("mddeactivate failed for %s" % device) + +def mdexamine(device): + # XXX NOTUSED: we grab metadata from udev, which ran 'mdadm -E --export' + # + # FIXME: this will not work with version >= 1 metadata + # + # We should use mdadm -Eb or mdadm -E --export for a more easily + # parsed output format. + lines = iutil.execWithCapture("mdadm", + ["--examine", device], + stderr="/dev/null").splitlines() + + info = { + 'major': "-1", + 'minor': "-1", + 'uuid' : "", + 'level': -1, + 'nrDisks': -1, + 'totalDisks': -1, + 'mdMinor': -1, + } + + for line in lines: + (key, sep, val) = line.strip().partition(" : ") + if not sep: + continue + if key == "Version": + (major, sep, minor) = val.partition(".") + info['major'] = major + info['minor'] = minor + elif key == "UUID": + info['uuid'] = val.split()[0] + elif key == "Raid Level": + info['level'] = int(val[4:]) + elif key == "Raid Devices": + info['nrDisks'] = int(val) + elif key == "Total Devices": + info['totalDisks'] = int(val) + elif key == "Preferred Minor": + info['mdMinor'] = int(val) + else: + continue + + if not info['uuid']: + raise MDRaidError("UUID missing from device info") + + return info + diff --git a/storage/devicelibs/swap.py b/storage/devicelibs/swap.py new file mode 100644 index 000000000..7d25bb3ed --- /dev/null +++ b/storage/devicelibs/swap.py @@ -0,0 +1,103 @@ +# swap.py +# Python module for managing swap devices. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# +import iutil +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +from errors import * + +def mkswap(device, label=''): + argv = [] + if label: + argv.extend(["-L", label]) + argv.append(device) + + rc = iutil.execWithRedirect("mkswap", argv, + stderr = "/dev/null", + stdout = "/dev/null", + searchPath=1) + + if rc: + raise SwapError("mkswap failed for '%s'" % device) + +def swapon(device, priority=None): + pagesize = resource.getpagesize() + buf = None + if pagesize > 2048: + num = pagesize + else: + num = 2048 + try: + fd = os.open(device, os.O_RDONLY) + buf = os.read(fd, num) + except: + pass + finally: + try: + os.close(fd) + except: + pass + + if buf is not None and len(buf) == pagesize: + sig = buf[pagesize - 10:] + if sig == 'SWAP-SPACE': + raise OldSwapError + if sig == 'S1SUSPEND\x00' or sig == 'S2SUSPEND\x00': + raise SuspendError + + argv = [] + if 0 <= priority <= 32767: + argv.extend(["-p", priority]) + argv.append(device) + + rc = iutil.execWithRedirect("swapon", + argv, + stderr = "/dev/null", + stdout = "/dev/null", + searchPath=1) + + if rc: + raise SwapError("swapon failed for '%s'" % device) + +def swapoff(device): + rc = iutil.execWithRedirect("swapoff", [device], + stderr = "/dev/null", + stdout = "/dev/null", + searchPath=1) + + if rc: + raise SwapError("swapoff failed for '%s'" % device) + +def swapstatus(device): + lines = open("/proc/swaps").readlines() + status = False + for line in lines: + if not line.strip(): + continue + + if line.split()[0] == device: + status = True + break + + return status + + diff --git a/storage/devices.py b/storage/devices.py new file mode 100644 index 000000000..d0cc8431b --- /dev/null +++ b/storage/devices.py @@ -0,0 +1,2338 @@ +# devices.py +# Device classes for anaconda's storage configuration module. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# + + +""" + Device classes for use by anaconda. + + This is the hierarchy of device objects that anaconda will use for + managing storage devices in the system. These classes will + individually make use of external support modules as needed to + perform operations specific to the type of device they represent. + + TODO: + - see how to do network devices (NetworkManager may help) + - perhaps just a wrapper here + - document return values of all methods/functions + - find out what other kinds of wild and crazy devices we need to + represent here (iseries? xen? more mainframe? mac? ps?) + - PReP + - this is a prime candidate for a PseudoDevice + - DASD + - ZFCP + - XEN + + What specifications do we allow? new existing + partitions + usage + + + filesystem, partition type are implicit + mountpoint + + + size + exact + - + range + - + resize - + + format - + + encryption + + + + disk + exact + - + set + - + how will we specify this? + partition w/ multiple parents cannot otherwise occur + primary + - + + mdraid sets + filesystem (*) + + + mountpoint + + + size? + format - + + encryption + + + + level + ? + device minor + ? + member devices + ? + spares + ? + name? + bitmap? (boolean) + - + + volume groups + name + - + member pvs + + + pesize + ? + + logical volumes + filesystem + + + mountpoint + + + size + exact + ? + format - + + encryption + + + + name + ? + vgname + ? + + +""" +import os +import math + +# device backend modules +import mdraid +import lvm +#import block +import dm + +# XXX temporary +import sys +sys.path.insert(0, "/root/pyparted/src") +sys.path.insert(1, "/root/pyparted/src/.libs") +sys.path.append("devicelibs") +import parted + +from errors import * +from iutil import log_method_call, notify_kernel, numeric_type +from udev import udev_settle +from deviceformat import get_device_format_class, getFormat + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("storage") + +def get_device_majors(): + majors = {} + for line in open("/proc/devices").readlines(): + try: + (major, device) = line.split() + except ValueError: + continue + try: + majors[int(major)] = device + except ValueError: + continue + return majors +device_majors = get_device_majors() + +class Device(object): + """ A generic device. + + Device instances know which devices they depend upon (parents + attribute). They do not know which devices depend upon them, but + they do know whether or not they have any dependent devices + (isleaf attribute). + + A Device's setup method should set up all parent devices as well + as the device itself. It should not run the resident format's + setup method. + + Which Device types rely on their parents' formats being active? + DMCryptDevice + + A Device's teardown method should accept the keyword argument + recursive, which takes a boolean value and indicates whether or + not to recursively close parent devices. + + A Device's create method should create all parent devices as well + as the device itself. It should also run the Device's setup method + after creating the device. The create method should not create a + device's resident format. + + Which device type rely on their parents' formats to be created + before they can be created/assembled? + VolumeGroup + DMCryptDevice + + A Device's destroy method should destroy any resident format + before destroying the device itself. + + """ + _type = "generic device" + _packages = [] + + def __init__(self, name, parents=None, description=''): + """ Create a Device instance. + + Arguments: + + name -- the device name (generally a device node's basename) + + Keyword Arguments: + + parents -- a list of required Device instances + description -- a string describing the device + + """ + self._name = name + if parents is None: + parents = [] + elif not isinstance(parents, list): + raise ValueError("parents must be a list of Device instances") + self.parents = parents + self.kids = 0 + self.description = description + + for parent in self.parents: + parent.addChild() + + def removeChild(self): + log_method_call(self, name=self.name, kids=self.kids) + self.kids -= 1 + + def addChild(self): + log_method_call(self, name=self.name, kids=self.kids) + self.kids += 1 + + def setup(self, intf=None): + """ Open, or set up, a device. """ + raise NotImplementedError("setup method not defined for Device") + + def teardown(self, recursive=None): + """ Close, or tear down, a device. """ + raise NotImplementedError("teardown method not defined for Device") + + def create(self, intf=None): + """ Create the device. """ + raise NotImplementedError("create method not defined for Device") + + def destroy(self): + """ Destroy the device. """ + raise NotImplementedError("destroy method not defined for Device") + + def setupParents(self): + """ Run setup method of all parent devices. """ + for parent in self.parents: + parent.setup() + + def teardownParents(self, recursive=None): + """ Run teardown method of all parent devices. """ + for parent in self.parents: + parent.teardown(recursive=recursive) + + def createParents(self): + """ Run create method of all parent devices. """ + log.info("NOTE: recursive device creation disabled") + for parent in self.parents: + if not parent.exists: + raise DeviceError("parent device does not exist") + #parent.create() + + def dependsOn(self, dep): + """ Return True if this device depends on dep. """ + # XXX does a device depend on itself? + if dep in self.parents: + return True + + for parent in self.parents: + if parent.dependsOn(dep): + return True + + return False + + @property + def status(self): + """ This device's status. + + For now, this should return a boolean: + True the device is open and ready for use + False the device is not open + """ + return False + + @property + def name(self): + """ This device's name. """ + return self._name + + @property + def isleaf(self): + """ True if this device has no children. """ + return self.kids == 0 + + @property + def typeDescription(self): + """ String describing the device type. """ + return self._type + + @property + def type(self): + """ Device type. """ + return self._type + + @property + def packages(self): + """ List of packages required to manage devices of this type. + + This list includes the packages required by this device's + format type as well those required by all of its parent + devices. + """ + packages = self._packages + packages.extend(self.format.packages) + for parent in parents: + for package in parent.packages: + if package not in packages: + packages.append(package) + + for package in parent.format.packages: + if package not in packages: + packages.append(package) + + return packages + + +class NetworkDevice(Device): + """ A network device """ + _type = "network device" + + def __init__(self, name, parents=None): + """ Create a NetworkDevice instance. + + Arguments: + + name -- the device name (generally an interface name) + + Keyword Arguments: + + parents -- a list of required Device instances + description -- a string describing the device + + """ + Device.__init__(self, name, parents=parents) + self.active = False + + +class StorageDevice(Device): + """ A generic storage device. + + A fully qualified path to the device node can be obtained via the + path attribute, although it is not guaranteed to be useful, or + even present, unless the StorageDevice's setup method has been + run. + + StorageDevice instances can optionally contain a filesystem, + represented by an FS instance. A StorageDevice's create method + should create a filesystem if one has been specified. + """ + _type = "storage device" + devDir = "/dev" + sysfsBlockDir = "/class/block" + _resizable = False + + def __init__(self, device, format=None, + size=None, major=None, minor=None, + sysfsPath='', parents=None, exists=None): + """ Create a StorageDevice instance. + + Arguments: + + device -- the device name (generally a device node's basename) + + Keyword Arguments: + + size -- the device's size (units/format TBD) + major -- the device major + minor -- the device minor + sysfsPath -- sysfs device path + format -- a DeviceFormat instance + parents -- a list of required Device instances + description -- a string describing the device + + """ + # allow specification of individual parents + if isinstance(parents, Device): + parents = [parents] + + Device.__init__(self, device, parents=parents) + + self.uuid = None + self.format = None + self._size = numeric_type(size) + self.major = numeric_type(major) + self.minor = numeric_type(minor) + self.sysfsPath = sysfsPath + self.exists = exists + + # this may be handy for disk, dmraid, mpath, mdraid + self.diskLabel = None + + self.format = format + self.fstabComment = "" + + @property + def path(self): + """ Device node representing this device. """ + return "%s/%s" % (self.devDir, self.name) + + def probe(self): + """ Probe for any missing information about this device. """ + raise NotImplementedError("probe method not defined for StorageDevice") + + def updateSysfsPath(self): + """ Update this device's sysfs path. """ + log_method_call(self, self.name, status=self.status) + self.sysfsPath = os.path.join("/sys", + self.sysfsBlockDir, + self.name) + log.debug("%s sysfsPath set to %s" % (self.name, self.sysfsPath)) + + @property + def formatArgs(self): + """ Device-specific arguments to format creation program. """ + return [] + + @property + def resizable(self): + """ Can this type of device be resized? """ + return self._resizable + + def notifyKernel(self): + """ Send a 'change' uevent to the kernel for this device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + log.debug("not sending change uevent for non-existent device") + return + + if not self.status: + log.debug("not sending change uevent for inactive device") + return + + self.updateSysfsPath() + path = os.path.normpath("/sys/%s" % self.sysfsPath) + try: + notify_kernel(path, action="change") + except Exception, e: + log.warning("failed to notify kernel of change: %s" % e) + + @property + def fstabSpec(self): + spec = self.path + if self.format and self.format.uuid: + spec = "UUID=%s" % self.format.uuid + return spec + + def resize(self, intf=None): + """ Resize the device. + + New size should already be set. + """ + raise NotImplementedError("resize method not defined for StorageDevice") + + def setup(self, intf=None): + """ Open, or set up, a device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + self.setupParents() + for parent in self.parents: + parent.format.setup() + + def teardown(self, recursive=None): + """ Close, or tear down, a device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + self.format.teardown() + + if recursive: + self.teardownParents(recursive=recursive) + + def _getSize(self): + """ Get the device's size. """ + return self._size + + def _setSize(self, newsize): + """ Set the device's size to a new value. """ + # FIXME: this should involve some verification + self._size = newsize + + size = property(lambda x: x._getSize(), lambda x, y: x._setSize(y), + doc="The device's size") + + @property + def status(self): + """ This device's status. + + For now, this should return a boolean: + True the device is open and ready for use + False the device is not open + """ + if not self.exists: + return False + return os.access(self.path, os.W_OK) + + def _setFormat(self, format): + """ Set the Device's format. """ + if not format: + format = getFormat(None, device=self.path) + log_method_call(self, self.name, type=format.type, + current=self._format.type, status=self.status) + if self.format and self._format.status: + # FIXME: self.format.status doesn't mean much + raise DeviceError("cannot replace active format") + + self._format = format + + def _getFormat(self): + return self._format + + format = property(lambda d: d._getFormat(), + lambda d,f: d._setFormat(f), + doc="The device's formatting.") + + def create(self, intf=None): + """ Create the device. """ + log_method_call(self, self.name, status=self.status) + if self.exists: + raise DeviceError("device has already been created") + + self.createParents() + self.setupParents() + self.setup() + self.exists = True + + def destroy(self): + """ Destroy the device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if not self.isleaf: + raise DeviceError("Cannot destroy non-leaf device") + + if self.status: + # best effort + self.format.destroy() + + self.exists = False + for parent in self.parents: + parent.removeChild() + + +class DiskDevice(StorageDevice): + """ A disk """ + _type = "disk" + + def __init__(self, device, format=None, + size=None, major=None, minor=None, + sysfsPath='', parents=None): + """ Create a DiskDevice instance. + + Arguments: + + device -- the device name (generally a device node's basename) + + Keyword Arguments: + + size -- the device's size (units/format TBD) + major -- the device major + minor -- the device minor + sysfsPath -- sysfs device path + format -- a DeviceFormat instance + parents -- a list of required Device instances + removable -- whether or not this is a removable device + + DiskDevices always exist. + """ + StorageDevice.__init__(self, device, format=format, size=size, + major=major, minor=minor, exists=True, + sysfsPath=sysfsPath, parents=parents) + + self.partedDevice = None + self.partedDisk = None + self.removable = False + if 'parted' in globals().keys(): + log.debug("looking up parted Device: %s" % self.path) + self.partedDevice = parted.Device(path=self.path) + if not self.partedDevice: + raise DeviceError("cannot find parted device instance") + log.debug("creating parted Disk: %s" % self.path) + self.partedDisk = parted.Disk(device=self.partedDevice) + if not self.partedDisk: + raise DeviceError("failed to create parted Disk") + + self.probe() + + #def open(self): + # raise DeviceError, "shit's busted." + + @property + def size(self): + """ The disk's size in MB """ + if not self._size: + self._size = self.partedDisk.device.getSize() + return self._size + + def setPartedDisk(self, disk): + log_method_call(self, self.name, disk_path=disk.device.path) + self.partedDisk = disk + + def removePartition(self, device): + partition = self.partedDisk.getPartitionByPath(device.path) + if partition: + self.partedDisk.removePartition(partition) + + def probe(self): + """ Probe for any missing information about this device. + + pyparted should be able to tell us anything we want to know. + size, disklabel type, maybe even partition layout + """ + if not 'parted' in globals().keys(): + return + + log_method_call(self, self.name, size=self.size, partedDevice=self.partedDevice) + if not self.size: + self.size = self.partedDevice.getSize() + if not self.diskLabel: + log.debug("setting %s diskLabel to %s" % (self.name, + self.partedDisk.type)) + self.diskLabel = self.partedDisk.type + + def create(self, intf=None): + """ Create the device. """ + log_method_call(self, self.name, status=self.status) + self.createParents() + self.setupParents() + self.partedDisk.commit() + self.setup() + + def destroy(self): + """ Destroy the device. """ + log_method_call(self, self.name, status=self.status) + if self.status + self.format.destroy() + + self.partedDisk.deleteAllPartitions() + # this is perhaps a separate operation (wiping the disklabel) + self.partedDisk.clobber() + self.partedDisk.commit() + self.teardown() + + for parent in self.parents: + parent.removeChild() + + def setup(self, intf=None): + """ Open, or set up, a device. """ + log_method_call(self, self.name, status=self.status) + if not os.path.exists(self.path): + raise DeviceError("device does not exist") + + def teardown(self, recursive=False): + """ Close, or tear down, a device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + +class PartitionDevice(StorageDevice): + """ A disk partition. + + On types and flags... + + We don't need to deal with numerical partition types at all. + + The only type we are concerned with is primary/logical/extended. + + Usage specification is accomplished through the use of flags, + which we will set according to the partition's format. + """ + _type = "partition" + _resizable = True + + def __init__(self, device, format=None, + size=None, grow=False, maxsize=None, + major=None, minor=None, bootable=None, + sysfsPath='', parents=None, exists=None, + partType=None, primary=False): + """ Create a PartitionDevice instance. + + Arguments: + + device -- the device name (generally a device node's basename) + + Keyword Arguments: + + exists -- indicates whether this is an existing device + format -- the device's format (DeviceFormat instance) + + For existing partitions: + + parents -- the disk that contains this partition + major -- the device major + minor -- the device minor + sysfsPath -- sysfs device path + + For new partitions: + + partType -- primary,extended,&c (as parted constant) + grow -- whether or not to grow the partition + maxsize -- max size for growable partitions (in MB) + size -- the device's size (in MB) + bootable -- whether the partition is bootable + parents -- a list of potential containing disks + """ + self.req_disks = [] + self.req_partType = None + self.req_primary = None + self.req_grow = None + self.req_bootable = None + self.req_size = 0 + self.req_base_size = 0 + self.req_max_size = 0 + + StorageDevice.__init__(self, device, format=format, size=size, + major=major, minor=minor, exists=exists, + sysfsPath=sysfsPath, parents=parents) + + if not exists: + # this is a request, not a partition -- it has no parents + self.req_disks = self.parents[:] + for dev in self.parents: + dev.removeChild() + self.parents = [] + + # FIXME: Validate partType, but only if this is a new partition + # Otherwise, overwrite it with the partition's type. + self._partType = None + self.partedFlags = {} + self._partedPartition = None + + # FIXME: Validate size, but only if this is a new partition. + # For existing partitions we will get the size from + # parted. + + if self.exists and 'parted' in globals().keys(): + log.debug("looking up parted Partition: %s" % self.path) + #self.partedPartition = parted.getPartitionByName(self.path) + self._partedPartition = self.disk.partedDisk.getPartitionByPath(self.path) + if not self._partedPartition: + raise DeviceError("cannot find parted partition instance") + + # collect information about the partition from parted + self.probe() + else: + # XXX It might be worthwhile to create a shit-simple + # PartitionRequest class and pass one to this constructor + # for new partitions. + self.req_name = name + self.req_partType = partType + self.req_primary = primary + self.req_max_size = numeric_type(maxsize) + self.req_grow = grow + self.req_bootable = bootable + + # req_size may be manipulated in the course of partitioning + self.req_size = self._size + + # req_base_size will always remain constant + self.req_base_size = self._size + + @property + def partType(self): + """ Get the partition's type (as parted constant). """ + if self.partedPartition: + return self.partedPartition.type + else: + if self.exists: + raise DeviceError("partition exists but has no partedPartition") + return self._partType + + @property + def isExtended(self): + return self.partType & parted.PARTITION_EXTENDED + + @property + def isLogical(self): + return self.partType & parted.PARTITION_LOGICAL + + @property + def isPrimary(self): + return self.partType == parted.PARTITION_NORMAL + + @property + def isProtected(self): + return self.partType & parted.PARTITION_PROTECTED + + @property + def getPartedPartition(self): + return self._partedPartition + log.debug("PartitionDevice %s has %d parents (%s)" % (self.name, + len(self.parents), self.parents)) + ppart = None + if len(self.parents) == 1: + if self._partedPartition and \ + self.disk.partedDisk == self._partedPartition.disk: + return self._partedPartition + + log.debug("path is %s ; partitions is %s" % (self.path, + [p.path for p in self.disk.partedDisk.partitions])) + pdisk = self.disk.partedDisk + ppart = pdisk.getPartitionByPath(self.path) + self._partedPartition = ppart + + return ppart + + def setPartedPartition(self, partition): + """ Set this PartitionDevice's parted Partition instance. """ + log_method_call(self, self.name) + if partition is None: + path = None + elif isinstance(partition, parted.Partition): + path = partition.path + else: + raise ValueError("partition must be a parted.Partition instance") + + log.debug("device %s new partedPartition %s has path %s" % (self.name, + partition, + path)) + self._partedPartition = partition + self._name = partition.getDeviceNodeName() + + partedPartition = property(lambda d: d.getPartedPartition(), + lambda d,p: d.setPartedPartition(p)) + + def dependsOn(self, dep): + """ Return True if this device depends on dep. """ + if dep.type == "partition" and dep.isExtended and self.isLogical: + return True + + return Device.dependsOn(self, dep) + + def _setFormat(self, format): + """ Set the Device's format. """ + log_method_call(self, self.name) + StorageDevice._setFormat(self, format) + + # now, set the flags on our parted.Partition + #if format: + # self.partedPartitions + + def setBootable(self, bootable): + """ Set the bootable flag for this partition. """ + if 'parted' not in globals().keys(): + return + + if self.partedPartition: + if isFlagAvailable(parted.PARTITION_BOOT): + if bootable: + self.partedPartition.setFlag(parted.PARTITION_BOOT) + else: + self.partedPartition.unsetFlag(parted.PARTITION_BOOT) + else: + raise DeviceError(_("boot flag not available for this " + "partition")) + else: + if self.partType != parted.PARTITION_NORMAL: + raise DeviceError(_("boot flag only available to primary " + "partitions")) + else: + self.bootable = bootable + + def probe(self): + """ Probe for any missing information about this device. + + size, partition type, flags + """ + log_method_call(self, self.name, exists=self.exists) + if not self.exists: + return + + # build a dict of this partition's parted flags + """ + XXX removed temporarily to make things faster + for flag in parted.partitionFlag.keys(): + if self.partedPartition.isFlagAvailable(flag): + self.partedFlags[flag] = self.partedPartition.getFlag(flag) + + if self.partedFlags[parted.PARTITION_BOOT]: + self.bootable = True + else: + self.bootable = False + """ + + # this is in MB + self.size = self.partedPartition.getSize() + + self._partType = self.partedPartition.type + + def create(self, intf=None): + """ Create the device. """ + log_method_call(self, self.name, status=self.status) + if self.exists: + raise DeviceError("device already exists") + + # first, do the parted commit + try: + self.disk.create() + except DeviceError, e: + log.debug("error creating %s: %s" % (self.parents[0].name, e)) + + self.setup() + self.exists = True + + def destroy(self): + """ Destroy the device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if not self.sysfsPath: + return + + if not self.isleaf: + raise DeviceError("Cannot destroy non-leaf device") + + if self.status: + self.format.destroy() + + # now actually tell pyparted to remove the partition + # FIXME: need special handling for extended partitions + parted.deletePartition(self.partedPartition) + + self.exists = False + for parent in self.parents: + parent.removeChild() + + def _getSize(self): + """ Get the device's size. """ + if self.partedPartition: + # this defaults to MB + return self.partedPartition.getSize() + else: + if self.exists: + raise DeviceError("partition exists but has no partedPartition") + return self._size + + def _setSize(self, newsize): + """ Set the device's size (for resize, not creation). + + Arguments: + + newsize -- the new size (in MB) + + """ + log_method_call(self, self.name, + status=self.status, size=self._size, newsize=newsize) + if not self.exists: + raise DeviceError("device does not exist") + + if newsize > self.disk.size: + raise ValueError("partition size would exceed disk size") + + # this defaults to MB + maxAvailableSize = self.partedPartition.getMaxAvailableSize() + + if newsize > maxAvailableSize: + raise ValueError("new size is greater than available space") + + # now convert the size to sectors and update the geometry + geometry = self.partedPartition.geometry + physicalSectorSize = geometry.device.physicalSectorSize + + new_length = (newsize * (1024 * 1024)) / physicalSectorSize + geometry.length = new_length + + def _getDisk(self): + """ The disk that contains this partition. """ + try: + disk = self.parents[0] + except IndexError: + disk = None + return disk + + def _setDisk(self, disk): + if self.disk: + self.disk.removeChild() + + self.parents = [disk] + disk.addChild() + + disk = property(lambda p: p._getDisk(), lambda p,d: p._setDisk(d)) + + +class DMDevice(StorageDevice): + """ A device-mapper device """ + _type = "dm" + devDir = "/dev/mapper" + + def __init__(self, name, format=None, size=None, dmUuid=None, + target=None, exists=None, parents=None, sysfsPath=''): + """ Create a DMDevice instance. + + Arguments: + + name -- the device name (generally a device node's basename) + + Keyword Arguments: + + target -- the device-mapper target type (string) + size -- the device's size (units/format TBD) + dmUuid -- the device's device-mapper UUID + sysfsPath -- sysfs device path + format -- a DeviceFormat instance + parents -- a list of required Device instances + exists -- indicates whether this is an existing device + """ + StorageDevice.__init__(self, name, format=format, size=size, + exists=exists, + parents=parents, sysfsPath=sysfsPath) + self.target = target + self.dmUuid = dmUuid + + def probe(self): + """ Probe for any missing information about this device. + + target type, parents? + """ + raise NotImplementedError("probe method not defined for DMDevice") + + @property + def fstabSpec(self): + """ Return the device specifier for use in /etc/fstab. """ + return self.path + + def updateSysfsPath(self): + """ Update this device's sysfs path. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if self.status: + dm_node = self.getDMNode() + self.sysfsPath = os.path.join("/sys", self.sysfsBlockDir, dm_node) + else: + self.sysfsPath = None + + #def getTargetType(self): + # return dm.getDmTarget(name=self.name) + + def getDMNode(self): + """ Return the dm-X (eg: dm-0) device node for this device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + return dm.dm_node_from_name(self.name) + #return block.getDmNodeFromName(self.name) + + @name.setter + def setName(self, name): + """ Set the device's map name. """ + log_method_call(self, self.name, status=self.status) + if self.status: + raise DeviceError("device is active") + + self._name = name + #self.sysfsPath = "/dev/disk/by-id/dm-name-%s" % self.name + + def teardown(self, recursive=None): + """ Close, or tear down, a device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if self.status: + self.format.teardown() + + if recursive: + self.teardownParents(recursive=recursive) + + +class DMCryptDevice(DMDevice): + """ A dm-crypt device """ + _type = "dm-crypt" + + def __init__(self, name, format=None, size=None, uuid=None, + exists=None, sysfsPath='', parents=None): + """ Create a DMCryptDevice instance. + + Arguments: + + name -- the device name (generally a device node's basename) + + Keyword Arguments: + + size -- the device's size (units/format TBD) + sysfsPath -- sysfs device path + format -- a DeviceFormat instance + parents -- a list of required Device instances + exists -- indicates whether this is an existing device + """ + DMDevice.__init__(self, name, format=format, size=size, + parents=parents, sysfsPath=sysfsPath, + exists=exists, target="crypt") + +class LUKSDevice(DMCryptDevice): + """ A mapped LUKS device. """ + _type = "luks/dm-crypt" + + def __init__(self, name, format=None, size=None, uuid=None, + exists=None, sysfsPath='', parents=None): + """ Create a LUKSDevice instance. + + Arguments: + + name -- the device name + + Keyword Arguments: + + size -- the device's size in MB + uuid -- the device's UUID + sysfsPath -- sysfs device path + format -- a DeviceFormat instance + parents -- a list of required Device instances + exists -- indicates whether this is an existing device + """ + DMCryptDevice.__init__(self, name, format=format, size=size, + parents=parents, sysfsPath=sysfsPath, + uuid=None, exists=exists) + + def create(self, intf=None): + """ Create the device. """ + log_method_call(self, self.name, status=self.status) + if self.exists: + raise DeviceError("device already exists") + + self.createParents() + self.setupParents() + + #if not self.slave.format.exists: + # self.slave.format.create() + self.setup() + self.exists = True + + def setup(self, intf=None): + """ Open, or set up, a device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + self.slave.setup() + self.slave.format.setup() + + def teardown(self, recursive=False): + """ Close, or tear down, a device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if self.status: + self.format.teardown() + self.slave.format.teardown() + + if recursive: + self.teardownParents(recursive=recursive) + + @property + def slave(self): + """ This device's backing device. """ + return self.parents[0] + + +class LVMVolumeGroupDevice(DMDevice): + """ An LVM Volume Group + + XXX Maybe this should inherit from StorageDevice instead of + DMDevice since there's no actual device. + """ + _type = "lvm vg" + + def __init__(self, name, parents, size=None, free=None, + peSize=None, peCount=None, peFree=None, pvCount=None, + lvNames=[], uuid=None, exists=None, sysfsPath=''): + """ Create a LVMVolumeGroupDevice instance. + + Arguments: + + name -- the device name (generally a device node's basename) + parents -- a list of physical volumes (StorageDevice) + + Keyword Arguments: + + peSize -- physical extent size (in MB) + exists -- indicates whether this is an existing device + sysfsPath -- sysfs device path + + For existing VG's only: + + size -- the VG's size (in MB) + free -- amount of free space in the VG + peFree -- number of free extents + peCount -- total number of extents + pvCount -- number of PVs in this VG + lvNames -- the names of this VG's LVs + uuid -- the VG's UUID + + """ + self.pvClass = get_device_format_class("lvmpv") + if not self.pvClass: + raise DeviceError("cannot find 'lvmpv' class") + + if isinstance(parents, list): + for dev in parents: + if not isinstance(dev.format, self.pvClass): + raise ValueError("constructor requires a list of PVs") + elif not isinstance(parents.format, self.pvClass): + raise ValueError("constructor requires a list of PVs") + + DMDevice.__init__(self, name, parents=parents, + exists=exists, sysfsPath=sysfsPath) + + self.uuid = uuid + self.free = numeric_type(free) + self.peSize = numeric_type(peSize) + self.peCount = numeric_type(peCount) + self.peFree = numeric_type(peFree) + self.pvCount = numeric_type(pvCount) + self.lvNames = lvNames + + # circular references, here I come + self._lvs = [] + + # TODO: validate peSize if given + if not self.peSize: + self.peSize = 4.0 # MB + + #self.probe() + + def probe(self): + """ Probe for any information about this device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + @property + def status(self): + """ The device's status (True means active). """ + if not self.exists: + return False + + # certainly if any of this VG's LVs are active then so are we + for lvName in self.lvNames: + lvPath = os.path.normpath("%s-%s" % (self.path, lvName)) + if os.path.exists(lvPath): + return True + + # if any of our PVs are not active then we cannot be + for pv in self.pvs: + if not pv.status: + return False + + # if we are missing some of our PVs we cannot be active + if len(self.pvs) != self.pvCount: + return False + + return True + + def addDevice(self, device): + """ Add a new physical volume device to the volume group. + + XXX This is for use by device probing routines and is not + intended for modification of the VG. + """ + log_method_call(self, + self.name, + device=device.name, + status=self.status) + if not self.exists: + raise DeviceError("device does not exist") + + if not isinstance(device.format, self.pvClass): + raise ValueError("addDevice requires a PV arg") + + if self.uuid and device.format.vgUuid != self.uuid: + raise ValueError("UUID mismatch") + + if device in self.pvs: + raise ValueError("device is already a member of this VG") + + self.parents.append(device) + device.addChild() + + # now see if the VG can be activated + if len(self.parents) == self.pvCount: + self.setup() + + def removeDevice(self, device): + """ Remove a physical volume from the volume group. + + This is for cases like clearing of preexisting partitions. + """ + log_method_call(self, + self.name, + device=device.name, + status=self.status) + try: + self.parents.remove(device) + except ValueError, e: + raise ValueError("cannot remove non-member PV device from VG") + + device.removeChild() + + def setup(self, intf=None): + """ Open, or set up, a device. + + XXX we don't do anything like "vgchange -ay" because we don't + want all of the LVs activated, just the VG itself. + """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if self.status: + return + + if len(self.parents) < self.pvCount: + raise DeviceError("cannot activate VG with missing PV(s)") + + self.setupParents() + + def teardown(self, recursive=None): + """ Close, or tear down, a device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if self.status: + lvm.vgdeactivate(self.name) + + if recursive: + self.teardownParents(recursive=recursive) + + def create(self, intf=None): + """ Create the device. """ + log_method_call(self, self.name, status=self.status) + if self.exists: + raise DeviceError("device already exists") + + pv_list = [] + #for pv in self.parents: + # This is a little bit different from other devices in that + # for VG we need the PVs to be formatted before we can create + # the VG. + # pv.create() + # pv.format.create() + # pv_list.append(pv.path) + pv_list = [pv.path for pv in self.parents] + self.createParents() + self.setupParents() + lvm.vgcreate(self.name, pv_list, self.peSize) + self.notifyKernel() + self.exists = True + self.setup() + + def destroy(self): + """ Destroy the device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + self.teardown() + + lvm.vgremove(self.name) + self.notifyKernel() + self.exists = False + for parent in self.parents: + parent.removeChild() + + def reduce(self, pv_list): + """ Remove the listed PVs from the VG. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + lvm.vgreduce(self.name, pv_list) + # XXX do we need to notify the kernel? + + def _addLogVol(self, lv): + """ Add an LV to this VG. """ + if lv in self._lvs: + raise ValueError("lv is already part of this vg") + + # verify we have the space, then add it + if lv.size > self.freeSpace: + raise DeviceError("new lv is too large to fit in free space") + + self._lvs.append(lv) + + def _removeLogVol(self, lv): + """ Remove an LV from this VG. """ + if not lv in self.lvs: + raise ValueError("specified lv is not part of this vg") + + self._lvs.remove(lv) + + def _addPV(self, pv): + """ Add a PV to this VG. """ + if pv in self.pvs: + raise ValueError("pv is already part of this vg") + + # for the time being we will not allow vgextend + if self.exists: + raise DeviceError("cannot add pv to existing vg") + + self.parents.append(pv) + pv.addChild() + + def _removePV(self, pv): + """ Remove an PV from this VG. """ + if not pv in self.pvs: + raise ValueError("specified pv is not part of this vg") + + # for the time being we will not allow vgreduce + if self.exists: + raise DeviceError("cannot remove pv from existing vg") + + self.parents.remove(pv) + pv.removeChild() + + """ We can't rely on lvm to tell us about our size, free space, &c + since we could have modifications queued, unless the VG and all of + its PVs already exist. + + -- liblvm may contain support for in-memory devices + """ + @property + def isModified(self): + """ Return True if the VG has changes queued that LVM is unaware of. """ + modified = True + if self.exists and not filter(lambda d: not d.exists, self.pvs): + modified = False + + return modified + + @property + def size(self): + """ The size of this VG """ + # TODO: just ask lvm if isModified returns False + + # sum up the sizes of the PVs and align to pesize + size = 0 + for pv in self.pvs: + size += self.align(pv.size - pv.format.peStart) + + return size + + @property + def extents(self): + """ Number of extents in this VG """ + # TODO: just ask lvm if isModified returns False + + return self.size / self.peSize + + @property + def freeSpace(self): + """ The amount of free space in this VG (in MB). """ + # TODO: just ask lvm if isModified returns False + + # total the sizes of any LVs + # FIXME: need to account for metadata + used = 0 + for lv in self.lvs: + used += self.align(lv.size) + + return self.size - used + + @property + def freeExtents(self): + """ The number of free extents in this VG. """ + # TODO: just ask lvm if isModified returns False + + # FIXME: need to account for metadata + return self.freeSpace / self.peSize + + def align(self, size): + """ Align a size to a multiple of physical extent size. """ + size = numeric_type(size) + + # we want Kbytes as a float for our math + size *= 1024.0 + pesize = self.peSize * 1024.0 + return long((math.floor(size / pesize) * pesize) / 1024) + + @property + def pvs(self): + """ A list of this VG's PVs """ + return self.parents[:] # we don't want folks changing our list + + @property + def lvs(self): + """ A list of this VG's LVs """ + return self._lvs[:] # we don't want folks changing our list + + +class LVMLogicalVolumeDevice(DMDevice): + """ An LVM Logical Volume """ + _type = "lvm lv" + _resizable = True + + def __init__(self, name, vgdev, size=None, uuid=None, + format=None, exists=None, sysfsPath='', + grow=None, maxsize=None, percent=None): + """ Create a LVMLogicalVolumeDevice instance. + + Arguments: + + name -- the device name (generally a device node's basename) + vgdev -- volume group (LVMVolumeGroupDevice instance) + + Keyword Arguments: + + size -- the device's size (in MB) + uuid -- the device's UUID + sysfsPath -- sysfs device path + format -- a DeviceFormat instance + exists -- indicates whether this is an existing device + + For new (non-existent) LVs only: + + grow -- whether to grow this LV + maxsize -- maximum size for growable LV (in MB) + percent -- percent of VG space to take + + """ + if isinstance(vgdev, list): + if len(vgdev) != 1: + raise ValueError("constructor requires a single LVMVolumeGroupDevice instance") + elif not isinstance(vgdev[0], LVMVolumeGroupDevice): + raise ValueError("constructor requires a LVMVolumeGroupDevice instance") + elif not isinstance(vgdev, LVMVolumeGroupDevice): + raise ValueError("constructor requires a LVMVolumeGroupDevice instance") + DMDevice.__init__(self, name, size=size, format=format, + sysfsPath=sysfsPath, parents=vgdev, + exists=exists) + + self.uuid = uuid + + self.req_grow = None + self.req_max_size = 0 + self.req_size = 0 + self.req_percent = 0 + + if not self.exists: + self.req_grow = grow + self.req_max_size = numeric_type(maxsize) + # XXX should we enforce that req_size be pe-aligned? + self.req_size = self._size + self.req_percent = numeric_type(percent) + + # here we go with the circular references + self.vg._addLogVol(self) + + def probe(self): + """ Probe for any missing information about this device. + + size + """ + raise NotImplementedError("probe method not defined for StorageDevice") + + @property + def vg(self): + """ This Logical Volume's Volume Group. """ + return self.parents[0] + + @property + def name(self): + """ This device's name. """ + return "%s-%s" % (self.vg.name, self._name) + + @property + def lvname(self): + """ The LV's name (not including VG name). """ + return self._name + + def setup(self, intf=None): + """ Open, or set up, a device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if self.status: + return + + self.vg.setup() + lvm.lvactivate(self.vg.name, self._name) + + def teardown(self, recursive=None): + """ Close, or tear down, a device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if self.status: + self.format.teardown() + lvm.lvdeactivate(self.vg.name, self._name) + + if recursive: + self.vg.teardown(recursive=recursive) + + def create(self, intf=None): + """ Create the device. """ + log_method_call(self, self.name, status=self.status) + if self.exists: + raise DeviceError("device already exists") + + self.createParents() + self.setupParents() + + # should we use --zero for safety's sake? + lvm.lvcreate(self.vg.name, self._name, self.size) + self.exists = True + self.setup() + + def destroy(self): + """ Destroy the device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if self.status: + self.format.destroy() + + self.teardown() + lvm.lvremove(self.vg.name, self._name) + self.exists = False + for parent in self.parents: + parent.removeChild() + + def resize(self, intf=None): + # XXX should we set up anything before doing this? + # XXX resize format probably, right? + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + self.format.teardown() + lvm.lvresize(self.vg.name, self._name, self.size) + + +class MDRaidArrayDevice(StorageDevice): + """ An mdraid (Linux RAID) device. + + Since this is derived from StorageDevice, not PartitionDevice, it + can be used to represent a partitionable device. + """ + _type = "mdarray" + + def __init__(self, name, level=None, minor=None, size=None, + memberDevices=None, totalDevices=None, bitmap=False, + uuid=None, format=None, exists=None, + parents=None, sysfsPath=''): + """ Create a MDRaidArrayDevice instance. + + Arguments: + + name -- the device name (generally a device node's basename) + + Keyword Arguments: + + level -- the device's RAID level (a string, eg: '1' or 'raid1') + parents -- list of member devices (StorageDevice instances) + size -- the device's size (units/format TBD) + uuid -- the device's UUID + minor -- the device minor + bitmap -- whether to use a bitmap (boolean) + sysfsPath -- sysfs device path + format -- a DeviceFormat instance + exists -- indicates whether this is an existing device + """ + StorageDevice.__init__(self, name, format=format, exists=exists, + minor=minor, size=size, + parents=parents, sysfsPath=sysfsPath) + self.level = level + self.uuid = uuid + self.memberDevices = numeric_type(memberDevices) + self.totalDevices = numeric_type(totalDevices) + self.sysfsPath = "/devices/virtual/block/%s" % name + + """ FIXME: Bitmap is more complicated than this. + + It can be internal or external. External requires a filename. + """ + self.bitmap = bitmap + + self.formatClass = get_device_format_class("mdmember") + if not self.formatClass: + raise DeviceError("cannot find class for 'mdmember'") + + #self.probe() + + @property + def mdadmConfEntry(self): + """ This array's mdadm.conf entry. """ + if not self.level or self.memberDevices is None or not self.uuid: + raise DeviceError("array is not fully defined") + + fmt = "ARRAY level=%s num-devices=%d UUID=%s" + return fmt % (self.level, self.memberDevices, self.uuid) + + def _getSpares(self): + spares = 0 + if self.memberDevices is not None: + if self.totalDevices is not None: + spares = self.totalDevices - self.memberDevices + else: + spares = self.memberDevices + self.totalDevices = self.memberDevices + return spares + + def _setSpares(self, spares): + # FIXME: this is too simple to be right + if self.totalDevices > spares: + self.memberDevices = self.totalDevices - spares + + spares = property(_getSpares, _setSpares) + + def probe(self): + """ Probe for any missing information about this device. + + I'd like to avoid paying any attention to "Preferred Minor" + as it seems problematic. + """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + try: + self.devices[0].setup() + except Exception: + return + + info = mdraid.mdexamine(self.devices[0].path) + if self.level is None: + self.level = info['level'] + if self.memberDevices is None: + self.memberDevices = info['nrDisks'] + if self.totalDevices is None: + self.totalDevices = info['totalDisks'] + if self.uuid is None: + self.uuid = info['uuid'] + if self.minor is None: + self.minor = info['mdMinor'] + + @property + def fstabSpec(self): + return self.path + + def updateSysfsPath(self): + """ Update this device's sysfs path. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if self.status: + self.sysfsPath = os.path.join("/sys", + self.sysfsBlockDir, + self.name) + else: + self.sysfsPath = None + + def addDevice(self, device): + """ Add a new member device to the array. + + XXX This is for use when probing devices, not for modification + of arrays. + """ + log_method_call(self, + self.name, + device=device.name, + status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if not isinstance(device.format, self.formatClass): + raise ValueError("invalid device format for mdraid member") + + if self.uuid and device.format.mdUuid != self.uuid: + raise ValueError("cannot add member with non-matching UUID") + + if device in self.devices: + raise ValueError("device is already a member of this array") + + # we added it, so now set up the relations + self.devices.append(device) + device.addChild() + + def removeDevice(self, device): + """ Remove a component device from the array. + + XXX This is for use by clearpart, not for reconfiguration. + """ + log_method_call(self, + self.name, + device=device.name, + status=self.status) + + if device not in self.devices: + raise ValueError("cannot remove non-member device from array") + + self.devices.remove(device) + device.removeChild() + + @property + def status(self): + """ This device's status. + + For now, this should return a boolean: + True the device is open and ready for use + False the device is not open + """ + # check the status in sysfs + status = False + if not self.exists: + return status + + state_file = "/sys/%s/md/array_state" % self.sysfsPath + if os.access(state_file, os.R_OK): + state = open(state_file).read().strip() + log.debug("%s state is %s" % (self.name, state)) + if state in ("clean", "active"): + status = True + + return status + + @property + def degraded(self): + """ Return True if the array is running in degraded mode. """ + rc = False + degraded_file = "/sys/%s/md/degraded" % self.sysfsPath + if os.access(state_file, os.R_OK): + val = open(state_file).read().strip() + log.debug("%s degraded is %s" % (self.name, val)) + if val == "1": + rc = True + + return rc + + @property + def devices(self): + """ Return a list of this array's member device instances. """ + return self.parents + + def setup(self, intf=None): + """ Open, or set up, a device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if self.status: + return + + disks = [] + for member in self.devices: + member.setup() + disks.append(member.path) + + mdraid.mdactivate(self.path, + members=disks, + super_minor=self.minor, + uuid=self.uuid) + + udev_settle() + + def teardown(self, recursive=None): + """ Close, or tear down, a device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if self.status: + self.format.teardown() + mdraid.mddeactivate(self.path) + + if recursive: + self.teardownParents(recursive=recursive) + + def create(self, intf=None): + """ Create the device. """ + log_method_call(self, self.name, status=self.status) + if self.exists: + raise DeviceError("device already exists") + + disks = [disk.path for disk in self.devices] + self.createParents() + self.setupParents() + spares = len(self.devices) - self.memberDevices + mdraid.mdcreate(self.path, + self.level, + disks, + spares) + self.exists = True + # the array is automatically activated upon creation, but... + self.setup() + udev_settle() + + # FIXME: we need to find our UUID now that the array exists + + @property + def formatArgs(self): + formatArgs = [] + if self.format.type == "ext2": + if self.level == 5: + formatArgs = ['-R', + 'stride=%d' % ((self.memberDevices - 1) * 16)] + elif self.level == 0: + formatArgs = ['-R', + 'stride=%d' % (self.memberDevices * 16)] + + def destroy(self): + """ Destroy the device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if self.status: + self.format.destroy() + + self.teardown() + # destruction of the formats on members will destroy the array + for disk in self.devices: + disk.format.destroy() + + self.exists = False + for parent in self.parents: + parent.removeChild() + + +class DMRaidArrayDevice(DMDevice): + """ A dmraid (device-mapper RAID) device """ + _type = "dm-raid array" + _packages = ["dmraid"] + + def __init__(self, name, format=None, size=None, + exists=None, parents=None, sysfsPath=''): + """ Create a DMRaidArrayDevice instance. + + Arguments: + + name -- the device name (generally a device node's basename) + + Keyword Arguments: + + parents -- a list of the member devices + sysfsPath -- sysfs device path + size -- the device's size + format -- a DeviceFormat instance + exists -- indicates whether this is an existing device + """ + if isinstance(parents, list): + for parent in parents: + if not parent.format or parent.format.type != "dmraidmember": + raise ValueError("parent devices must contain dmraidmember format") + DMDevice.__init__(self, name, format=format, size=size, + parents=parents, sysfsPath=sysfsPath, + exists=exists) + + def probe(self): + """ Probe for any missing information about this device. + + size + """ + raise NotImplementedError("probe method not defined for DMRaidArrayDevice") + + +class MultipathDevice(DMDevice): + """ A multipath device """ + _type = "dm-multipath" + _packages = ["device-mapper-multipath"] + + def __init__(self, name, format=None, size=None, + exists=None, parents=None, sysfsPath=''): + """ Create a MultipathDevice instance. + + Arguments: + + name -- the device name (generally a device node's basename) + + Keyword Arguments: + + sysfsPath -- sysfs device path + size -- the device's size + format -- a DeviceFormat instance + parents -- a list of the backing devices (Device instances) + exists -- indicates whether this is an existing device + """ + DMDevice.__init__(self, name, format=format, size=size, + parents=parents, sysfsPath=sysfsPath, + exists=exists) + + def probe(self): + """ Probe for any missing information about this device. + + size + """ + raise NotImplementedError("probe method not defined for MultipathDevice") + + +class NoDevice(StorageDevice): + """ A nodev device for nodev filesystems like tmpfs. """ + _type = "nodev" + + def __init__(self, format=None): + """ Create a NoDevice instance. + + Arguments: + + Keyword Arguments: + + format -- a DeviceFormat instance + """ + if format: + name = format.type + else: + name = "nodev" + + StorageDevice.__init__(self, name, format=format) + + @property + def path(self): + """ Device node representing this device. """ + return self.name + + def probe(self): + """ Probe for any missing information about this device. """ + log_method_call(self, self.name, status=self.status) + + def setup(self, intf=None): + """ Open, or set up, a device. """ + log_method_call(self, self.name, status=self.status) + + def teardown(self, recursive=False): + """ Close, or tear down, a device. """ + log_method_call(self, self.name, status=self.status) + + def create(self, intf=None): + """ Create the device. """ + log_method_call(self, self.name, status=self.status) + self.setupParents() + + def destroy(self): + """ Destroy the device. """ + log_method_call(self, self.name, status=self.status) + + +class FileDevice(StorageDevice): + """ A file on a filesystem. + + This exists because of swap files. + """ + _type = "file" + devDir = "" + + def __init__(self, path, format=None, size=None, + exists=None, parents=None): + """ Create a FileDevice instance. + + Arguments: + + path -- full path to the file + + Keyword Arguments: + + format -- a DeviceFormat instance + size -- the file size (units TBD) + parents -- a list of required devices (Device instances) + exists -- indicates whether this is an existing device + """ + StorageDevice.__init__(self, name, format=format, size=size, + exists=exists, parents=parents) + + def probe(self): + """ Probe for any missing information about this device. """ + pass + + @property + def fstabSpec(self): + return self.path + + @property + def path(self): + path = self.name + root = "" + try: + status = self.parents[0].format.status + except AttributeError: + status = False + + if status: + # this is the actual active mountpoint + root = self.parents[0].format._mountpoint + + return os.path.normpath("%s/%s" % (root, path)) + + def setup(self): + StorageDevice.setup(self) + if self.format and self.format.exists and not self.format.status: + self.format.device = self.path + + for parent in self.parents: + parent.format.setup() + + def teardown(self): + StorageDevice.teardown(self) + if self.format and self.format.exists and not self.format.status: + self.format.device = self.path + + def create(self, intf=None): + """ Create the device. """ + log_method_call(self, self.name, status=self.status) + if self.exists: + raise DeviceError("device already exists") + + # this only checks that parents exist + self.createParents() + self.setupParents() + + try: + fd = os.open(self.path, os.O_RDWR) + except OSError as e: + raise DeviceError(e) + + try: + buf = '\0' * 1024 * 1024 * self.size + os.write(fd, buf) + except Exception, e: + log.error("error zeroing out %s: %s" % (self.device, e)) + finally: + os.close(fd) + + self.exists = True + + def destroy(self): + """ Destroy the device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + if self.status: + self.format.destroy() + + os.unlink(self.path) + self.exists = False + for parent in self.parents: + parent.removeChild() + + +class DirectoryDevice(FileDevice): + """ A directory on a filesystem. + + This exists because of bind mounts. + """ + _type = "directory" + + def create(self): + """ Create the device. """ + log_method_call(self, self.name, status=self.status) + if self.exists: + raise DeviceError("device already exists") + + self.createParents() + self.setupParents() + try: + iutil.mkdirChain(self.path) + except Exception, e: + raise DeviceError, e + + self.exists = True + + def destroy(self): + """ Destroy the device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + os.unlink(self.path) + self.exists = False + for parent in self.parents: + parent.removeChild() + + +class iScsiDiskDevice(DiskDevice): + """ An iSCSI volume/device/target/???. + + TODO: learn what this is and how we need to use it. + """ + _type = "iscsi" + + def __init__(self, ipaddr, port, + user=None, passwd=None, + user_in=None, passwd_in=None, + major=None, minor=None, size=None, + exists=None, parents=None, sysfsPath=''): + name = "iscsi://%s:%s" % (ipaddr, port) + DiskDevice.__init__(self, name, size=size, + major=major, minor=minor, exists=exists, + parents=parents, sysfsPath=sysfsPath) + + def probe(self): + """ Probe for any missing information about this device. """ + raise NotImplementedError("probe method not defined for StorageDevice") + + +class OpticalDevice(StorageDevice): + """ An optical drive, eg: cdrom, dvd+r, &c. + + XXX Is this useful? + """ + _type = "cdrom" + + def __init__(self, name, major=None, minor=None, exists=None, + format=None, parents=None, sysfsPath=''): + StorageDevice.__init__(self, name, format=format, + major=major, minor=minor, exists=True, + parents=parents, sysfsPath=sysfsPath) + + def teardown(self, recursive=None): + """ Close, or tear down, a device. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + self.format.teardown() + if recursive: + self.teardownParents(recursive=recursive) + + def mediaPresent(self): + """ Return a boolean indicating whether or not the device contains + media. + """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + def eject(self): + """ Eject the drawer. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") + + +class ZFCPDiskDevice(DiskDevice): + """ A mainframe ZFCP disk. """ + _type = "zfcp" + + def __init__(self, name, size=None, major=None, minor=None, + devnum=None, wwpn=None, fcplun=None, + parents=None, sysfsPath=''): + self.devnum = devnum + self.wwpn = wwpn + self.fcplun = fcplun + name = "zfcp://%s/%s/%s" % (self.devnum, self.wwpn, self.fcplun) + DiskDevice.__init__(self, name, size=size, + major=major, minor=minor, + parents=parents, sysfsPath=sysfsPath) + + def probe(self): + """ Probe for any missing information about this device. + + devnum, wwpn, fcplun + """ + raise NotImplementedError("probe method not defined for StorageDevice") + + +class DASDDevice(DiskDevice): + """ A mainframe DASD. """ + _type = "dasd" + + def __init__(self, device, size=None, major=None, minor=None, + parents=None, sysfsPath=''): + DiskDevice.__init__(self, device, size=size, + major=major, minor=minor, + parents=parents, sysfsPath=sysfsPath) + + def probe(self): + """ Probe for any missing information about this device. """ + raise NotImplementedError("probe method not defined for StorageDevice") + + +class PRePBootDevice(PartitionDevice): + """ A PPC PReP boot partition. + + XXX Would this be better represented by a DeviceFormat class? + """ + _type = "PReP" + #_partedFlags = parted.PARTITION_PREP + + def __init__(self, device, + size=None, grow=False, maxsize=None, + major=None, minor=None, + sysfsPath='', parents=None, + exists=None, primary=False): + """ Create a PRePBootDevice instance. + + Arguments: + + device -- the device name (generally a device node's basename) + + Keyword Arguments: + + grow -- whether or not to grow the partition (boolean ) + maxsize -- max size for growable partitions (units TBD) + size -- the device's size (units/format TBD) + major -- the device major + minor -- the device minor + sysfsPath -- sysfs device path + parents -- a list of required Device instances + exists -- indicates whether this is an existing device + """ + PartitionDevice.__init__(self, device, partType=self._partType, + size=size, grow=grow, maxsize=maxsize, + major=major, minor=minor, + sysfsPath=sysfsPath, exists=exists, + parents=parents, primary=primary) + + +class NFSDevice(StorageDevice): + """ An NFS device """ + _type = "nfs" + + def __init__(self, device, format=None, parents=None): + # we could make host/ip, path, &c but will anything use it? + StorageDevice.__init__(device, format=format, parents=parents) + + @property + def path(self): + """ Device node representing this device. """ + return self.name + + def setup(self, intf=None): + """ Open, or set up, a device. """ + log_method_call(self, self.name, status=self.status) + + def teardown(self, recursive=None): + """ Close, or tear down, a device. """ + log_method_call(self, self.name, status=self.status) + + def create(self, intf=None): + """ Create the device. """ + log_method_call(self, self.name, status=self.status) + self.createParents() + self.setupParents() + + def destroy(self): + """ Destroy the device. """ + log_method_call(self, self.name, status=self.status) + + diff --git a/storage/devicetree.py b/storage/devicetree.py new file mode 100644 index 000000000..da1a60f71 --- /dev/null +++ b/storage/devicetree.py @@ -0,0 +1,999 @@ +# devicetree.py +# Device management for anaconda's storage configuration module. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# + +import os + +import sys +sys.path.append("formats") + +if __name__ == "__main__": + import storage_log + +from errors import * +from devices import * +from deviceaction import * +from deviceformat import getFormat +from udev import * + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("storage") + +def getLUKSPassphrase(intf, device, globalPassphrase): + """ Obtain a passphrase for a LUKS encrypted block device. + + The format's mapping name must already be set and the backing + device must already be set up before calling this function. + + If successful, this function leaves the device mapped. + + Return value is a two-tuple: (passphrase, isglobal) + + passphrase is the passphrase string, if obtained + isglobal is a boolean indicating whether the passphrase is global + + Either or both can be None, depending on the outcome. + """ + if device.format.type != "luks": + # this function only works on luks devices + raise ValueError("not a luks device") + + if not device.status: + # the device should have already been set up + raise RuntimeError("device is not set up") + + if device.format.status: + # the device is already mapped + raise RuntimeError("device is already mapped") + + if not device.format.configured and passphrase: + # try the given passphrase first + device.format.passphrase = globalPassphrase + + try: + device.format.setup() + except CryptoError as e: + device.format.passphrase = None + else: + # we've opened the device so we're done. + return (globalPassphrase, False) + + buttons = [_("Back"), _("Continue")] + while True: + if passphrase_incorrect: + # TODO: add a flag to passphraseEntryWindow to say the last + # passphrase was incorrect so try again + passphrase_incorrect = False + (passphrase, isglobal) = intf.passphraseEntryWindow(device.name) + 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.") % device.name, + type = "custom", + default = 0, + custom_buttons = buttons) + if rc == 0: + continue + else: + passphrase = None + isglobal = None + log.info("skipping passphrase for %s" % (device.name,)) + break + + try: + device.format.setup() + except CryptoError as e: + device.format.passphrase = None + passphrase_incorrect = True + else: + # we've opened the device so we're done. + break + + return (passphrase, isglobal) + + +class DeviceTree(object): + """ A quasi-tree that represents the devices in the system. + + The tree contains a list of device instances, which does not + necessarily reflect the actual state of the system's devices. + DeviceActions are used to perform modifications to the tree, + except when initially populating the tree. + + DeviceAction instances are registered, possibly causing the + addition or removal of Device instances to/from the tree. The + DeviceActions are all reversible up to the time their execute + method has been called. + + Only one action of any given type/object pair should exist for + any given device at any given time. + + DeviceAction instances can only be registered for leaf devices, + except for resize actions. + """ + + def __init__(self, intf=None, ignored=[], exclusive=[], + zeroMbr=None, passphrase=None, luksDict=None): + # internal data members + self._devices = [] + self._actions = [] + + self.intf = intf + self.ignoredDisks = ignored + self.exclusiveDisks = exclusive + self.zeroMbr = zeroMbr + self.__passphrase = passphrase + self.__luksDevs = {} + if luksDict and isinstance(luksDict, dict): + self.__luksDevs = luksDict + + self._populate() + + def processActions(self, dryRun=None): + """ Execute all registered actions. """ + def cmpActions(x, y): + """ + < 1 => x < y + 0 => x == y + > 1 => x > y + + FIXME: this is unmanageable. + """ + #log.debug("%s | %s" % (x, y)) + + # destroy actions come first + if x.isDestroy() and not y.isDestroy(): + #log.debug("x is destroy -- x first") + return -1 + elif y.isDestroy() and not x.isDestroy(): + #log.debug("y is destroy -- y first") + return 1 + elif x.isDestroy() and y.isDestroy(): + # outermost/youngest first + if x.device.dependsOn(y.device): + #log.debug("x depends on y -- x first") + return -1 + elif y.device.dependsOn(x.device): + #log.debug("y depends on x -- y first") + return 1 + # filesystem destruction must precede device destruction + elif x.isFormat() and not y.isFormat(): + #log.debug("x is format -- x first") + return -1 + elif y.isFormat() and not x.isFormat(): + #log.debug("y is format -- y first") + return 1 + + # resize actions come next + if x.isResize() and not y.isResize(): + #log.debug("x is resize -- x first") + return -1 + elif y.isResize() and not x.isResize(): + #log.debug("y is resize -- y first") + return 1 + elif x.isResize() and y.isResize(): + if x.isGrow() and y.isGrow(): + # for grow, devices come first, root down + if x.isDevice() and not y.isDevice(): + #log.debug("x is device -- x first") + return -1 + elif y.isDevice() and not x.isDevice(): + #log.debug("y is device -- y first") + return 1 + else: + # innermost/oldest first + if x.device.dependsOn(y.device): + #log.debug("x depends on y -- y first") + return 1 + elif y.device.dependsOn(x.device): + #log.debug("y depends on x -- x first") + return -1 + elif x.isShrink() and y.isShrink(): + # for shrink, filesystems come first, leaves up + if x.isFormat() and not y.isFormat(): + #log.debug("x is format -- x first") + return -1 + elif y.isFormat() and not x.isFormat(): + #log.debug("y is format -- y first") + return 1 + else: + # outermost/youngest first + if x.device.dependsOn(y.device): + #log.debug("x depends on y -- x first") + return -1 + elif y.device.dependsOn(x.device): + #log.debug("y depends on x -- y first") + return 1 + else: + # we don't care about grow action -v- shrink action + # since they should be unrelated + #log.debug("we don't care") + return 0 + + # create actions come last + if x.isCreate() and y.isCreate(): + # innermost/oldest first + if x.device.dependsOn(y.device): + #log.debug("x depends on y") + return 1 + elif y.device.dependsOn(x.device): + #log.debug("y depends on x") + return -1 + # devices first, root down + elif x.isDevice() and not y.isDevice(): + #log.debug("x is a device") + return -1 + elif y.isDevice() and not x.isDevice(): + #log.debug("y is a device") + return 1 + elif x.device.isleaf and not y.device.isleaf: + #log.debug("x is a leaf -- y first") + return 1 + elif y.device.isleaf and not x.device.isleaf: + #log.debug("y is a leaf -- x first") + return -1 + + #log.debug("no decision") + return 0 + + # in most cases the actions will already be sorted because of the + # rules for registration, but let's not rely on that + self._actions.sort(cmpActions) + for action in self._actions: + log.info("executing action: %s" % action) + if not dryRun: + action.execute(intf=self.intf) + + def _addDevice(self, newdev): + """ Add a device to the tree. + + Raise ValueError if the device's identifier is already + in the list. + """ + if newdev.path in [d.path for d in self._devices]: + raise ValueError("device is already in tree") + + # make sure this device's parent devices are in the tree already + for parent in newdev.parents: + if parent not in self._devices: + raise DeviceTreeError("parent device not in tree") + + self._devices.append(newdev) + log.debug("added %s (%s) to device tree" % (newdev.name, + newdev.type)) + + def _removeDevice(self, dev, force=None): + """ Remove a device from the tree. + + Only leaves may be removed. + """ + if dev not in self._devices: + raise ValueError("Device '%s' not in tree" % dev.name) + + if not dev.isleaf and not force: + log.debug("%s has %d kids" % (dev.name, dev.kids)) + raise ValueError("Cannot remove non-leaf device '%s'" % dev.name) + + # if this is a partition we need to remove it from the parted.Disk + if dev.type == "partition": + dev.disk.removePartition(dev) + + self._devices.remove(dev) + log.debug("removed %s (%s) from device tree" % (dev.name, + dev.type)) + + for parent in dev.parents: + # Will this cause issues with garbage collection? + # Do we care about garbage collection? At all? + parent.removeChild() + + def registerAction(self, action): + """ Register an action to be performed at a later time. + + Modifications to the Device instance are handled before we + get here. + """ + removedAction = None + for _action in self._actions: + if _action.device == action.device and \ + _action.type == action.type and \ + _action.obj == action.obj: + #raise DeviceTreeError("duplicate action for this device") + log.debug("cancelling action '%s' in favor of '%s'" % (_action, + action) + self.cancelAction(_action) + removedAction = _action + break + + if (action.isDestroy() or action.isResize() or \ + (action.isCreate() and action.isFormat())) and \ + action.device not in self._devices: + raise DeviceTreeError("device is not in the tree") + elif (action.isCreate() and action.isDevice()) and \ + action.device in self._devices: + raise DeviceTreeError("device is already in the tree") + + if action.isCreate() and action.isDevice(): + self._addDevice(action.device) + elif action.isDestroy() and action.isDevice(): + self._removeDevice(action.device) + elif action.isCreate() and action.isFormat(): + if isinstance(action.device.format, FS) and \ + action.device.format.mountpoint in self.filesystems: + raise DeviceTreeError("mountpoint already in use") + + log.debug("registered action: %s" % action) + self._actions.append(action) + return removedAction + + def cancelAction(self, action): + """ Cancel a registered action. + + This will unregister the action and do any required + modifications to the device list. + + Actions all operate on a Device, so we can use the devices + to determine dependencies. + """ + if action.isCreate() and action.isDevice(): + # remove the device from the tree + self._removeDevice(action.device) + elif action.isDestroy() and action.isDevice(): + # add the device back into the tree + self._addDevice(action.device) + + def getDependentDevices(self, dep): + """ Return a list of devices that depend on dep. + + The list includes both direct and indirect dependents. + """ + dependents = [] + for device in self.devices.values(): + if device.dependsOn(dep): + dependents.append(device) + + return dependents + + def isIgnored(self, info): + """ Return True if info is a device we should ignore. + + Arguments: + + info -- a dict representing a udev db entry + + TODO: + + - filtering of SAN/FC devices + - filtering by driver? + + """ + sysfs_path = udev_device_get_sysfs_path(info) + name = udev_device_get_name(info) + if not sysfs_path: + return None + + if name in self.ignoredDisks: + return True + + for ignored in self.ignoredDisks: + if ignored == os.path.basename(os.path.dirname(sysfs_path)): + # this is a partition on a disk in the ignore list + return True + + # FIXME: check for virtual devices whose slaves are on the ignore list + + def addUdevDevice(self, info): + # FIXME: this should be broken up into more discrete chunks + name = udev_device_get_name(info) + uuid = udev_device_get_uuid(info) + sysfs_path = udev_device_get_sysfs_path(info) + device = None + + if self.isIgnored(sysfs_path): + log.debug("ignoring %s (%s)" % (name, sysfs_path)) + return + + log.debug("scanning %s (%s)..." % (name, sysfs_path)) + + # + # The first step is to either look up or create the device + # + if udev_device_is_dm(info): + log.debug("%s is a device-mapper device" % name) + # try to look up the device + device = self.getDeviceByName(name) + if device is None and uuid: + # try to find the device by uuid + device = self.getDeviceByUuid(uuid) + + if device is None: + for dmdev in self.devices: + if not isinstance(dmdev, DMDevice): + continue + + # there is a device in the tree already with the same + # major/minor as this one but with a different name + # XXX this is kind of racy + if dmdev.getDMNode() == os.path.basename(sysfs_path): + # XXX should we take the name already in use? + device = dmdev + break + + if device is None: + # we couldn't find it, so create it + # first, get a list of the slave devs and look them up + slaves = [] + dir = os.path.normpath("/sys/%s/slaves" % sysfs_path) + slave_names = os.listdir(dir) + for slave_name in slave_names: + # if it's a dm-X name, resolve it to a map name first + if slave_name.startswith("dm-"): + #slave_name = block.getNameFromDmNode(slave_name) + slave_name = dm.name_from_dm_node(slave_name) + slave_dev = self.getDeviceByName(slave_name) + if slave_dev: + slaves.append(slave_dev) + else: + # we haven't scanned the slave yet, so do it now + path = os.path.normpath("%s/%s" % (dir, slave_name)) + new_info = udev_get_block_device(os.path.realpath(path)) + if new_info: + self.addUdevDevice(new_info) + device = self.getDeviceByName(name) + if device is None: + # if the current device is still not in + # the tree, something has gone wrong + log.error("failure scanning device %s" % name) + return + + # if we get here, we found all of the slave devices and + # something must be wrong -- if all of the slaves are in + # the tree, this device should be as well + if device is None: + log.warning("using generic DM device for %s" % name) + device = DMDevice(name, exists=True, parents=slaves) + self._addDevice(device) + elif udev_device_is_md(info): + log.debug("%s is an md device" % name) + # try to look up the device + device = self.getDeviceByName(name) + if device is None and uuid: + # try to find the device by uuid + device = self.getDeviceByUuid(uuid) + + if device is None: + # we didn't find a device instance, so we will create one + slaves = [] + dir = os.path.normpath("/sys/%s/slaves" % sysfs_path) + slave_names = os.listdir(dir) + for slave_name in slave_names: + # if it's a dm-X name, resolve it to a map name + if slave_name.startswith("dm-"): + #slave_name = block.getNameFromDmNode(slave_name) + slave_name = dm.name_from_dm_node(slave_name) + slave_dev = self.getDeviceByName(slave_name) + if slave_dev: + slaves.append(slave_dev) + else: + # we haven't scanned the slave yet, so do it now + path = os.path.normpath("%s/%s" % (dir, slave_name)) + new_info = udev_get_block_device(os.path.realpath(path)) + if new_info: + self.addUdevDevice(new_info) + device = self.getDeviceByName(name) + if device is None: + # if the current device is still not in + # the tree, something has gone wrong + log.error("failure scanning device %s" % name) + return + + # if we get here, we found all of the slave devices and + # something must be wrong -- if all of the slaves we in + # the tree, this device should be as well + if device is None: + log.warning("using MD RAID device for %s" % name) + try: + # level is reported as, eg: "raid1" + md_level = udev_device_get_md_level(info) + md_devices = int(udev_device_get_md_devices(info)) + md_uuid = udev_device_get_md_uuid(info) + except (KeyError, IndexError, ValueError) as e: + log.warning("invalid data for %s: %s" % (name, e)) + return + + device = MDRaidArrayDevice(name, + level=md_level, + memberDevices=md_devices, + uuid=md_uuid, + exists=True, + parents=slaves) + self._addDevice(device) + elif udev_device_is_cdrom(info): + log.debug("%s is a cdrom" % name) + device = self.getDeviceByName(name) + if device is None: + # XXX should this be RemovableDevice instead? + # + # Looks like if it has ID_INSTANCE=0:1 we can ignore it. + device = OpticalDevice(name, + major=udev_device_get_major(info), + minor=udev_device_get_minor(info), + sysfsPath=sysfs_path) + self._addDevice(device) + elif udev_device_is_disk(info): + log.debug("%s is a disk" % name) + device = self.getDeviceByName(name) + if device is None: + device = DiskDevice(name, + major=udev_device_get_major(info), + minor=udev_device_get_minor(info), + sysfsPath=sysfs_path) + self._addDevice(device) + elif udev_device_is_partition(info): + log.debug("%s is a partition" % name) + device = self.getDeviceByName(name) + if device is None: + disk_name = os.path.basename(os.path.dirname(sysfs_path)) + disk = self.getDeviceByName(disk_name) + + if disk is None: + # create a device instance for the disk + path = os.path.dirname(os.path.realpath(sysfs_path)) + new_info = udev_get_block_device(path) + if new_info: + self.addUdevDevice(new_info) + disk = self.getDeviceByName(disk_name) + + if disk is None: + # if the current device is still not in + # the tree, something has gone wrong + log.error("failure scanning device %s" % disk_name) + return + + device = PartitionDevice(name, + sysfsPath=sysfs_path, + major=udev_device_get_major(info), + minor=udev_device_get_minor(info), + exists=True, + parents=[disk]) + self._addDevice(device) + + # + # now set the format + # + format = None + format_type = udev_device_get_format(info) + label = udev_device_get_label(info) + if device and format_type and not device.format: + args = [format_type] + kwargs = {"uuid": uuid, + "label": label, + "device": device.path, + "exists": True} + + if format_type == "swap": + # swap + pass + elif format_type == "crypto_LUKS": + # luks/dmcrypt + kwargs["mapName"] = "luks-%s" % uuid + elif format_type == "linux_raid_member": + # mdraid + kwargs["mdUuid"] = udev_device_get_md_uuid(info) + elif format_type == "isw_raid_member": + # dmraid + # TODO: collect name of containing raidset + # TODO: implement dmraid member format class + pass + elif format_type == "LVM2_member": + # lvm + try: + kwargs["vgName"] = udev_device_get_vg_name(info) + except KeyError as e: + log.debug("PV %s has no vg_name" % name) + kwargs["vgUuid"] = udev_device_get_vg_uuid(info) + kwargs["peStart"] = udev_device_get_pv_pe_start(info) + + device.format = getFormat(*args, **kwargs) + + # + # now lookup or create any compound devices we have discovered + # + if format: + if format.type == "luks": + if not format.uuid: + log.info("luks device %s has no uuid" % device.path) + return + + # look up or create the mapped device + if not self.getDeviceByName(device.format.mapName): + passphrase = self.__luksDevs.get(format.uuid) + if passphrase: + format.passphrase = passphrase + else: + (passphrase, isglobal) = getLUKSPassphrase(self.intf, + device, + self.__passphrase) + if isglobal and format.status: + self.__passphrase = passphrase + + luks_device = LUKSDevice(device.format.MapName, + parents=[device], + exists=True) + self._addDevice(luks_device) + try: + luks_device.setup() + except DeviceError as e: + log.info("setup of %s failed: %s" % (map_name, e)) + else: + log.warning("luks device %s already in the tree" % map_name) + elif format.type == "mdmember": + # either look up or create the array device + md_array = self.getDeviceByUuid(format.mdUuid) + if format.mdUuid and md_array: + md_array._addDevice(device) + else: + # create the array with just this one member + # FIXME: why does this exact block appear twice? + try: + # level is reported as, eg: "raid1" + md_level = udev_device_get_md_level(info) + md_devices = int(udev_device_get_md_devices(info)) + md_uuid = udev_device_get_md_uuid(info) + except (KeyError, ValueError) as e: + log.warning("invalid data for %s: %s" % (name, e)) + return + + # find the first unused minor + minor = 0 + while True: + if self.getDeviceByName("md%d" % minor): + minor += 1 + else: + break + + md_name = "md%d" % minor + md_array = MDRaidArrayDevice(md_name, + level=md_level, + minor=minor, + memberDevices=md_devices, + uuid=md_uuid, + exists=True, + parents=[device]) + self._addDevice(md_array) + try: + md_array.setup() + except DeviceError as e: + log.info("setup of %s failed: %s" % (md_array.name, e)) + elif format.type == "dmraidmember": + # look up or create the dmraid array + pass + elif format.type == "lvmpv": + # lookup/create the VG and LVsA + try: + vg_name = udev_device_get_vg_name(info) + except KeyError: + # no vg name means no vg -- we're done with this pv + return + + vg_device = self.getDeviceByName(vg_name) + if vg_device: + vg_device._addDevice(device) + else: + try: + vg_uuid = udev_device_get_vg_uuid(info) + vg_size = udev_device_get_vg_size(info) + vg_free = udev_device_get_vg_free(info) + pe_size = udev_device_get_vg_extent_size(info) + pe_count = udev_device_get_vg_extent_count(info) + pe_free = udev_device_get_vg_free_extents(info) + pv_count = udev_device_get_vg_pv_count(info) + except (KeyError, ValueError) as e: + log.warning("invalid data for %s: %s" % (name, e)) + return + + vg_device = LVMVolumeGroupDevice(vg_name, + device, + uuid=vg_uuid, + size=vg_size, + free=vg_free, + peSize=pe_size, + peCount=pe_count, + peFree=pe_free, + pvCount=pv_count, + exists=True) + self._addDevice(vg_device) + + try: + lv_names = udev_device_get_lv_names(info) + lv_uuids = udev_device_get_lv_uuids(info) + lv_sizes = udev_device_get_lv_sizes(info) + except KeyError as e: + log.warning("invalid data for %s: %s" % (name, e)) + return + + if not lv_names: + log.debug("no LVs listed for VG %s" % name) + return + + lvs = [] + for (index, lv_name) in enumerate(lv_names): + name = "%s-%s" % (vg_name, lv_name) + lv_dev = self.getDeviceByName(name) + if lv_dev is None: + lv_uuid = lv_uuids[index] + lv_size = lv_sizes[index] + lv_device = LVMLogicalVolumeDevice(lv_name, + vg_device, + uuid=lv_uuid, + size=lv_size, + exists=True) + self._addDevice(lv_device) + try: + lv_device.setup() + except DeviceError as e: + log.info("setup of %s failed: %s" + % (lv_device.name, e)) + + def _populate(self): + """ Locate all storage devices. """ + # each iteration scans any devices that have appeared since the + # previous iteration + old_devices = [] + ignored_devices = [] + while True: + devices = [] + new_devices = udev_get_block_devices() + + for new_device in new_devices: + found = False + for old_device in old_devices: + if old_device['name'] == new_device['name']: + found = True + break + + if not found: + devices.append(new_device) + + if len(devices) == 0: + # nothing is changing -- we are finished building devices + break + + old_devices = new_devices + log.debug("devices to scan: %s" % [d['name'] for d in devices]) + for dev in devices: + self.addUdevDevice(dev) + + def teardownAll(self): + """ Run teardown methods on all devices. """ + for device in self.leaves: + try: + device.teardown(recursive=True) + except DeviceError as e: + log.info("teardown of %s failed: %s" % (device.name, e)) + + def setupAll(self): + """ Run setup methods on all devices. """ + for device in self.leaves: + try: + device.setup() + except DeviceError as e: + log.debug("setup of %s failed: %s" % (device.name, e)) + + def getDeviceBySysfsPath(self, path): + found = None + for device in self._devices: + if device.sysfsPath == path: + found = device + break + + return found + + def getDeviceByUuid(self, uuid): + found = None + for device in self._devices: + if device.uuid == uuid: + found = device + break + elif device.format.uuid == uuid: + found = device + break + + return found + + def getDevicebyLabel(self, label): + found = None + for device in self._devices: + _label = getattr(device.format, "label", None) + if not _label: + continue + + if _label == label: + found = device + break + + return found + + def getDeviceByName(self, name): + log.debug("looking for device '%s'..." % name) + found = None + for device in self._devices: + if device.name == name: + found = device + break + + log.debug("found %s" % found) + return found + + def getDevicesByType(self, device_type): + # TODO: expand this to catch device format types + return [d for d in self._devices if d.type == device_type] + + @property + def devices(self): + """ Dict with device path keys and Device values. """ + devices = {} + + for device in self._devices: + if device.path in devices: + raise DeviceTreeError("duplicate paths in device tree") + + devices[device.path] = device + + return devices + + @property + def filesystems(self): + """ List of filesystems. """ + #""" Dict with mountpoint keys and filesystem values. """ + filesystems = [] + for dev in self.leaves: + if dev.format and getattr(dev.format, 'mountpoint', None): + filesystems.append(dev.format) + + return filesystems + + @property + def uuids(self): + """ Dict with uuid keys and Device values. """ + uuids = {} + for dev in self._devices: + try: + uuid = dev.uuid + except AttributeError: + uuid = None + elif uuid: + uuids[uuid] = dev + + try: + uuid = dev.format.uuid + except AttributeError: + uuid = None + elif uuid: + uuids[uuid] = dev + + return uuids + + @property + def labels(self): + """ Dict with label keys and Device values. + + FIXME: duplicate labels are a possibility + """ + labels = {} + for dev in self._devices: + if dev.format and getattr(dev.format, "label", None): + labels[dev.format.label] = dev + + return labels + + @property + def leaves(self): + """ List of all devices upon which no other devices exist. """ + leaves = [d for d in self._devices if d.isleaf] + return leaves + + def getChildren(self, device): + """ Return a list of a device's children. """ + return [c for c in self._devices if device in c.parents] + + +def test_tree(): + try: + tree = DeviceTree(ignored_disks=[]) + except Exception as e: + log.error("tree creation failed: %s" % e) + raise + return tree + +def test_fstab(tree): + roots = findExistingRoots(tree, keepall=True) + print ["%s: %s" % (d.path, d.format.type) for d in roots] + rootDev = roots[0] + if not rootDev: + return + + log.debug("setting up root device %s" % rootDev.path) + #rootDev.setup() + #rootDev.format.mount(chroot="/mnt/sysimage", mountpoint="/") + #fstab = FSTab(tree, chroot="/mnt/sysimage") + fsset = FSSet(tree) + m + #return fstab + +if __name__ == "__main__": + mode = "tree" + if len(sys.argv) == 2: + mode = sys.argv[1] + + if mode == "tree": + tree = test_tree() + if tree is None: + sys.exit(1) + devices = tree.devices.values() + devices.sort(key=lambda d: d.path) + for device in devices: + fs_string = "" + if device.format: + fs_string = "%s on " % device.format.type + print "%s: %s%s" % (device.path, fs_string, device.typeDescription) + elif mode == "fstab": + tree = test_tree() + tree.teardownAll() + fstab = test_fstab(tree) + #print fstab.blkidTab.devices + #print fstab.cryptTab.mappings + fmt = "%-23s %-23s %-7s %-15s %s %s" + for in fstab.devices: + (device, mountpoint, fstype, options, dump, passno) = entry + print fmt % (device.fstabSpec(), mountpoint, fstype, + options, dump, passno) + + print + print "ORIGINAL:" + print fstab.origBuf + print + elif mode == "actions": + print "creating tree..." + tree = test_tree() + + # we don't need to actually use any of the devices, so clean up now + tree.teardownAll() + print "setting up actions..." + tree.registerAction(ActionDestroyFormat(tree.getDeviceByName("luks-md0"))) + tree.registerAction(ActionDestroyDevice(tree.getDeviceByName("luks-md0"))) + fs = getFormat("ext3", device="/dev/md0", mountpoint="/opt") + tree.registerAction(ActionCreateFormat(tree.getDeviceByName("md0"), fs)) + tree.registerAction(ActionDestroyFormat(tree.getDeviceByName("sda1"))) + print "processing actions..." + tree.processActions(dryRun=True) + print "done." + + tree.teardownAll() + diff --git a/storage/errors.py b/storage/errors.py new file mode 100644 index 000000000..3521873d3 --- /dev/null +++ b/storage/errors.py @@ -0,0 +1,99 @@ + +# Device +class DeviceError(Exception): + pass + +class DeviceCreateError(DeviceError): + pass + +class DeviceDestroyError(DeviceError): + pass + +class DeviceSetupError(DeviceError): + pass + +class DeviceTeardownError(DeviceError): + pass + +class DeviceResizeError(DeviceError): + pass + +# DeviceFormat +class DeviceFormatError(Exception): + pass + +class FormatCreateError(DeviceFormatError): + pass + +class FormatDestroyError(DeviceFormatError): + pass + +class FormatSetupError(DeviceFormatError): + pass + +class FormatTeardownError(DeviceFormatError): + pass + +class DMRaidMemberError(DeviceFormatError): + pass + +class FSError(DeviceFormatError): + pass + +class FSResizeError(FSError): + pass + +class FSMigrateError(FSError): + pass + +class LUKSError(DeviceFormatError): + pass + +class MDMemberError(DeviceFormatError): + pass + +class PhysicalVolumeError(DeviceFormatError): + pass + +class SwapSpaceError(DeviceFormatError): + pass + +# devicelibs +class SwapError(Exception): + pass + +class SuspendError(SwapError): + pass + +class OldSwapError(SwapError): + pass + +class MDRaidError(Exception): + pass + +class DMError(Exception): + pass + +class LVMError(Exception): + pass + +class CryptoError(Exception): + pass + +# DeviceTree +class DeviceTreeError(Exception): + pass + +# DeviceAction +class DeviceActionError(Exception): + pass + +# partitioning +class PartitioningError(Exception): + pass + +# udev +class UdevError(Exception): + pass + + diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py new file mode 100644 index 000000000..016b3eb73 --- /dev/null +++ b/storage/formats/__init__.py @@ -0,0 +1,308 @@ +# __init__.py +# Entry point for anaconda storage formats subpackage. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# + +import os + +from errors import * +from iutil import notify_kernel, get_sysfs_path_by_name, log_method_call +from dm import dm_node_from_name + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("storage") + + +device_formats = {} +def register_device_format(fmt_class): + if not issubclass(fmt_class, DeviceFormat): + raise ValueError("arg1 must be a subclass of DeviceFormat") + + device_formats[fmt_class._type] = fmt_class + log.debug("registered device format class %s as %s" % (fmt_class.__name__, + fmt_class._type)) + +default_fstypes = ("ext4", "ext3", "ext2") +default_boot_fstypes = ("ext3", "ext2") +def get_default_filesystem_type(boot=None): + if boot: + fstypes = default_boot_filesystem_types + else: + fstypes = default_filesystem_types + + for fstype in fstypes: + try: + supported = get_device_format_class(fstype).supported + except AttributeError: + supported = None + elif supported: + return fstype + + raise DeviceFormatError("None of %s is supported by your kernel" % ",".join(fstypes)) + +def getFormat(fmt_type, *args, **kwargs): + """ Return a DeviceFormat instance based on fmt_type and args. + + Given a device format type and a set of constructor arguments, + return a DeviceFormat instance. + + Return None if no suitable format class is found. + + Arguments: + + fmt_type -- the name of the format type (eg: 'ext3', 'swap') + + Keyword Arguments: + + The keyword arguments may vary according to the format type, + but here is the common set: + + device -- path to the device on which the format resides + uuid -- the UUID of the (preexisting) formatted device + exists -- whether or not the format exists on the device + + """ + fmt_class = get_device_format_class(fmt_type) + fmt = None + if fmt_class: + fmt = fmt_class(*args, **kwargs) + try: + className = fmt.__class__.__name__ + except AttributeError: + className = None + log.debug("getFormat('%s') returning %s instance" % (fmt_type, className)) + return fmt + +def collect_device_format_classes(): + """ Pick up all device format classes from this directory. + + Note: Modules must call register_device_format(FormatClass) in + order for the format class to be picked up. + """ + dir = os.path.dirname(__file__) + for module_file in os.listdir(dir): + if module_file.endswith(".py"): + mod_name = module_file[:-3] + # FIXME: use imputils here instead of exec + try: + exec("import %s" % mod_name) + except ImportError, e: + log.debug("import of device format module '%s' failed" % mod_name) + +def get_device_format_class(fmt_type): + """ Return an appropriate format class based on fmt_type. """ + if not device_formats: + collect_device_format_classes() + + fmt = device_formats.get(fmt_type) + if not fmt: + for fmt_class in device_formats.values(): + if fmt_type == fmt_class.name: + fmt = fmt_class + break + elif fmt_type in fmt_class._udevTypes: + fmt = fmt_class + break + + # default to no formatting, AKA "Unknown" + if not fmt: + fmt = DeviceFormat + + return fmt + +class DeviceFormat(object): + """ Generic device format. """ + _type = None + _name = "Unknown" + _udevTypes = [] + _formattable = False # can be formatted + _supported = False # is supported + _linuxNative = False # for clearpart + _packages = [] # required packages + _resizable = False # can be resized + _bootable = False # can be used as boot + _maxsize = 0 # maximum size in MB + _minsize = 0 # minimum size in MB + _dump = False + _check = False + + def __init__(self, *args, **kwargs): + """ Create a DeviceFormat instance. + + Keyword Arguments: + + device -- path to the underlying device + uuid -- this format's UUID + exists -- indicates whether this is an existing format + + """ + self.device = kwargs.get("device") + self.uuid = kwargs.get("uuid") + self.exists = kwargs.get("exists") + self.options = kwargs.get("options") + + def setDevice(self, devspec): + if devspec and not devspec.startswith("/"): + raise ValueError("device must be a fully qualified path") + self._device = devspec + + def getDevice(self): + return self._device + + device = property(lambda f: f.getDevice(), + lambda f,d: f.setDevice(d), + doc="Full path the device this format occupies") + + @property + def name(self): + if self._name: + name = self._name + else: + name = self.type + return name + + @property + def type(self): + return self._type + + def probe(self): + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + + def notifyKernel(self): + log_method_call(self, device=os.path.basename(self.device), + type=self.type) + if self.device.startswith("/dev/mapper/"): + try: + name = dm_node_from_name(os.path.basename(self.device)) + except Exception, e: + log.warning("failed to get dm node for %s" % self.device) + return + else: + name = os.path.basename(self.device) + + + path = get_sysfs_path_by_name(name) + try: + notify_kernel(path, action="change") + except Exception, e: + log.warning("failed to notify kernel of change: %s" % e) + + + def create(self, *args, **kwargs): + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + pass + + def destroy(self, *args, **kwargs): + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + # zero out the 1MB at the beginning and end of the device in the + # hope that it will wipe any metadata from filesystems that + # previously occupied this device + log.debug("zeroing out beginning and end of %s..." % self.device) + try: + fd = os.open(self.device, os.O_RDWR) + buf = '\0' * 1024 * 1024 + os.write(fd, buf) + os.lseek(fd, -1024 * 1024, 2) + os.write(fd, buf) + os.close(fd) + except Exception, e: + log.error("error zeroing out %s: %s" % (self.device, e)) + + def setup(self, *args, **kwargs): + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + pass + + def teardown(self, *args, **kwargs): + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + pass + + @property + def status(self): + if not self.exists: + return False + return os.path.exists(self.device) + + @property + def formattable(self): + """ Can we create formats of this type? """ + return self._formattable + + @property + def supported(self): + """ Is this format a supported type? """ + return self._supported + + @property + def packages(self): + """ Packages required to manage formats of this type. """ + return self._packages + + @property + def resizable(self): + """ Can formats of this type be resized? """ + return self._resizable + + @property + def bootable(self): + """ Is this format type suitable for a boot partition? """ + return self._bootable + + @property + def linuxNative(self): + """ Is this format type native to linux? """ + return self._linuxNative + + @property + def mountable(self): + """ Is this something we can mount? """ + return False + + @property + def dump(self): + """ Whether or not this format will be dumped by dump(8). """ + return self._dump + + @property + def check(self): + """ Whether or not this format is checked on boot. """ + return self._check + + @property + def maxsize(self): + """ Maximum size (in MB) for this format type. """ + return self._maxsize + + @property + def minsize(self): + """ Minimum size (in MB) for this format type. """ + return self._minsize + + +collect_device_format_classes() + + diff --git a/storage/formats/dmraid.py b/storage/formats/dmraid.py new file mode 100644 index 000000000..acf78da64 --- /dev/null +++ b/storage/formats/dmraid.py @@ -0,0 +1,89 @@ +# dmraid.py +# dmraid device formats +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# + +from iutil import log_method_call +#from dm import dm_node_from_name +from errors import * +from deviceformat import * + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("storage") + + +class DMRaidMember(DeviceFormat): + """ A dmraid member disk. """ + _type = "dmraidmember" + _name = "dm-raid member device" + # XXX This looks like trouble. + # + # Maybe a better approach is a RaidMember format with subclass + # for MDRaidMember, letting all *_raid_member types fall through + # to the generic RaidMember format, which is basically read-only. + # + # One problem that presents is the possibility of someone passing + # a dmraid member to the MDRaidArrayDevice constructor. + _udevTypes = ["adaptec_raid_member", "ddf_raid_member", + "highpoint_raid_member", "isw_raid_member", + "jmicron_raid_member", "lsi_mega_raid_member", + "nvidia_raid_member", "promise_fasttrack_raid_member", + "silicon_medley_raid_member", "via_raid_member"] + _formattable = False # can be formatted + _supported = True # is supported + _linuxNative = False # for clearpart + _packages = ["dmraid"] # required packages + _resizable = False # can be resized + _bootable = False # can be used as boot + _maxsize = 0 # maximum size in MB + _minsize = 0 # minimum size in MB + + def __init__(self, *args, **kwargs): + """ Create a DeviceFormat instance. + + Keyword Arguments: + + device -- path to the underlying device + uuid -- this format's UUID + raidSet -- the name of the raidset this member belongs to + exists -- indicates whether this is an existing format + + """ + log_method_call(self, *args, **kwargs) + DeviceFormat.__init__(self, *args, **kwargs) + + self.raidSet = kwargs.get("raidSet") + + def create(self, *args, **kwargs): + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + raise DMRaidMemberError("creation of dmraid members is not supported") + + def destroy(self, *args, **kwargs): + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + raise DMRaidMemberError("destruction of dmraid members is not supported") + + +register_device_format(DMRaidMember) + diff --git a/storage/formats/fs.py b/storage/formats/fs.py new file mode 100644 index 000000000..dcd340b59 --- /dev/null +++ b/storage/formats/fs.py @@ -0,0 +1,856 @@ +# filesystems.py +# Filesystem classes for anaconda's storage configuration module. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# + +""" Filesystem classes for use by anaconda. + + TODO: + - migration + - bug 472127: allow creation of tmpfs filesystems (/tmp, /var/tmp, &c) +""" +import os +import isys + +from errors import * +from deviceformat import * +import iutil + +import logging +log = logging.getLogger("storage") + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + + +fs_configs = {} + +def get_kernel_filesystems(): + fs_list = [] + for line in open("/proc/filesystems").readlines(): + fs_list.append(line.split()[-1]) + return fs_list +kernel_filesystems = get_kernel_filesystems() + +def fsFromConfig(attrs, *args, **kwargs): + """ Create an FS instance based on a set of attributes, passing on + constructor arguments. + """ + # XXX NOTUSED + if not attrs.has_key("type"): + raise ValueError, _("attr dict must include a type") + + fs = FS(*args, **kwargs) + for (attr, value) in attrs.items(): + setattr(fs, "_%s" % attr, value) + + if attrs["type"] in nodev_filesystems: + setattr(fs, "_nodev", True) + + return fs + +def fsConfigFromFile(config_file): + """ Generate a set of attribute name/value pairs with which a + filesystem type can be defined. + + The following config file would define a filesystem identical to + the static Ext3FS class definition: + + type = ext3 + mkfs = "mke2fs" + resizefs = "resize2fs" + labelfs = "e2label" + fsck = "e2fsck" + packages = ["e2fsprogs"] + formattable = True + supported = True + resizable = True + bootable = True + linuxNative = True + maxsize = 8 * 1024 * 1024 + minsize = 0 + defaultFormatOptions = "-t ext3" + defaultMountOptions = "defaults" + + """ + # XXX NOTUSED + lines = open(config_file).readlines() + fs_attrs = {} + for line in lines: + (key, value) = [t.strip() for t in line.split("=")] + if not hasattr(FS, "_" + key): + print "invalid key: %s" % key + continue + + fs_attrs[key] = value + + if not fs_attrs.has_key("type"): + raise ValueError, _("filesystem configuration missing a type") + + # XXX what's the policy about multiple configs for a given type? + fs_configs[fs_attrs['type']] = fs_attrs + +class FS(DeviceFormat): + """ Filesystem class. """ + _type = "filesystem" # fs type + _name = "Abstract Filesystem Class" # fs type name + _mkfs = "" # mkfs utility + _resizefs = "" # resize utility + _labelfs = "" # labeling utility + _fsck = "" # fs check utility + _defaultFormatOptions = [] # default options passed to mkfs + _defaultMountOptions = ["defaults"] # default options passed to mount + _defaultLabelOptions = [] + _defaultCheckOptions = [] + lostAndFoundContext = None + + def __init__(self, *args, **kwargs): + """ Create a FS instance. + + Keyword Args: + + device -- path to the device containing the filesystem + mountpoint -- the filesystem's mountpoint + label -- the filesystem label + uuid -- the filesystem UUID + mountopts -- mount options for the filesystem + size -- the filesystem's size in MiB + exists -- indicates whether this is an existing filesystem + + """ + if self.__class__ is FS: + raise TypeError("FS is an abstract class.") + + DeviceFormat.__init__(self, *args, **kwargs) + # TODO: fsprofiles and other ways to add format args + self.mountpoint = kwargs.get("mountpoint") + self.mountopts = kwargs.get("mountopts") + self.label = kwargs.get("label") + # filesystem size does not necessarily equal device size + self._size = kwargs.get("size") + self._targetSize = None + self._mountpoint = None # the current mountpoint when mounted + + def _setTargetSize(self, newsize): + """ Set a target size for this filesystem. """ + if not self.exists: + raise FSError("filesystem has not been created") + + if newsize is None: + # unset any outstanding resize request + self._targetSize = None + return + + if not self.minsize < newsize < self.maxsize: + raise ValueError("invalid target size request") + + self._targetSize = newsize + + def _getTargetSize(self): + """ Get this filesystem's target size. """ + return self._targetSize + + targetSize = property(_getTargetSize, _setTargetSize, + doc="Target size for this filesystem") + + def _getSize(self): + """ Get this filesystem's size. """ + return self._size + + size = property(_getSize, doc="This filesystem's size") + + def _getFormatArgs(self, options=None): + argv = [] + argv.extend(options) + argv.extend(self.defaultFormatOptions) + argv.append(self.device) + return argv + + def format(self, *args, **kwargs): + """ Create the filesystem. + + Arguments: + + None + + Keyword Arguments: + + intf -- InstallInterface instance + options -- list of options to pass to mkfs + + """ + log_method_call(self, type=self.type, device=self.device, + mountpoint=self.mountpoint) + + intf = kwargs.get("intf") + options = kwargs.get("options") + + if self.exists: + raise FormatCreateError("filesystem already exists", self.device) + + if not self.formattable(): + return + + if not self.mkfsProg: + return + + if self.exists: + return + + if not os.path.exists(self.device): + raise FormatCreateError("device does not exist", self.device) + + argv = self._getFormatArgs(options=options) + + w = None + if intf: + w = intf.progressWindow(_("Formatting"), + _("Creating filesystem on %s...") + % (self.device,), + 100, pulse = True) + + try: + rc = iutil.execWithPulseProgress(self.mkfsProg, + argv, + stderr="/dev/null", + progress=w) + except Exception as e: + raise FormatCreateError(e, self.device) + finally: + if w: + w.pop() + + if rc: + raise FormatCreateError("format failed: %s" % rc, self.device) + + self.exists = True + self.notifyKernel() + + def _getResizeArgs(self): + argv = [self.device, self.targetSize] + return argv + + def resize(self, *args, **kwargs): + """ Resize this filesystem to new size @newsize. + + Arguments: + + None + + Keyword Arguments: + + intf -- InstallInterface instance + + """ + intf = kwargs.get("intf") + + if not self.exists: + raise FSResizeError("filesystem does not exist", self.device) + + if not self.resizable: + # should this instead raise an exception? + return + + if self.targetSize is None: + # FIXME: implement minimum size method/attr and/or checking in + # setter for targetSize property + return + + if not self.resizefsProg: + return + + if not os.path.exists(self.device): + raise FSResizeError("device does not exist", self.device) + + self.check(intf=intf) + + argv = self._getResizeArgs() + + w = None + if intf: + w = intf.progressWindow(_("Resizing"), + _("Resizing filesystem on %s...") + % (self.device,), + 100, pulse = True) + + try: + rc = iutil.execWithPulseProgress(self.resizefsProg, + argv, + stderr="/dev/null", + progress=w) + except Exception as e: + raise FSResizeError(e, self.device) + finally: + if w: + w.pop() + + if rc: + raise FSResizeError("resize failed: %s" % rc, self.device) + + # XXX must be a smarter way to do this + self._size = self.targetSize + self.notifyKernel() + + def getMinimumSize(self): + raise NotImplementedError("getMinimumSize") + if not self.exists: + raise FSError("filesystem does not exist") + + def _getCheckArgs(self): + argv = [] + argv.extend(self.defaultCheckOptions) + argv.append(self.device) + + def check(self, intf=None): + if not self.exists: + raise FSError("filesystem has not been created") + + if not self.fsckProg: + return + + if not os.path.exists(self.device): + raise FSError("device does not exist") + + w = None + if intf: + w = intf.progressWindow(_("Checking"), + _("Checking filesystem on %s...") + % (self.device), + 100, pulse = True) + + try: + rc = iutil.execWithPulseProgress(self.fsckProg, + argv, + stdout="/dev/null", + stderr="/dev/null", + progress = w) + except Exception as e: + raise FSError("filesystem check failed: %s" % e) + finally: + if w: + w.pop() + + if rc >= 4: + raise FSError("filesystem check failed: %s" % rc) + + def mount(self, *args, **kwargs): + """ Mount this filesystem. + + Arguments: + + None + + Keyword Arguments: + + options -- mount options (overrides all other option strings) + chroot -- prefix to apply to mountpoint + mountpoint -- mountpoint (overrides self.mountpoint) + """ + options = kwargs.get("options", "") + chroot = kwargs.get("chroot", "/") + mountpoint = kwargs.get("mountpoint") + + if not self.exists: + raise FSError("filesystem has not been created") + + if not mountpoint: + mountpoint = self.mountpoint + + if not mountpoint: + raise FSError("no mountpoint given") + + if self.status: + raise FSError("filesystem is already mounted") + + if not isinstance(self, NoDevFS) and not os.path.exists(self.device): + raise FSError("device %s does not exist" % self.device) + + # XXX os.path.join is FUBAR: + # + # os.path.join("/mnt/foo", "/") -> "/" + # + #mountpoint = os.path.join(chroot, mountpoint) + mountpoint = os.path.normpath("%s/%s" % (chroot, mountpoint)) + iutil.mkdirChain(mountpoint) + if flags.selinux: + ret = isys.resetFileContext(mountpoint) + log.info("set SELinux context for mountpoint %s to %s" \ + % (mountpoint, ret)) + + # passed in options override default options + if not options or not isinstance(options, str): + options = ",".join(self.defaultMountOptions)) + + try: + rc = isys.mount(self.device, mountpoint, + fstype=self.type, + options=options, + bindMount=isinstance(self, BindFS)) + except Exception as e: + raise FSError("mount failed: %s" % e) + + if rc: + raise FSError("mount failed: %s" % rc) + + if flags.selinux: + ret = isys.resetFileContext(mountpoint) + log.info("set SELinux context for newly mounted filesystem " + "root at %s to %s" %(mountpoint, ret)) + if FS.lostAndFountContext is None: + FS.lostAndFoundContext = isys.matchPathContext("/lost+found") + isys.setFileContext("%s/lost+found" % mountpoint, + FS.lostAndFoundContext) + + self._mountpoint = mountpoint + + def unmount(self): + """ Unmount this filesystem. """ + if not self.exists: + raise FSError("filesystem has not been created") + + if not self._mountpoint: + # not mounted + return + + if not os.path.exists(self._mountpoint): + raise FSError("mountpoint does not exist") + + rc = isys.umount(self._mountpoint) + if rc: + raise FSError("umount failed") + + self._mountpoint = None + + def _getLabelArgs(self, label): + argv = [] + argv.extend(self.defaultLabelOptions) + argv.extend([self.device, label]) + return argv + + def writeLabel(self, label): + """ Create a label for this filesystem. """ + if not self.exists: + raise FSError("filesystem has not been created") + + if not self.labelfsProg: + return + + if not os.path.exists(self.device): + raise FSError("device does not exist") + + argv = self._getLabelArgs(label) + rc = iutil.execWithRedirect(self.labelfsProg, + argv, + stderr="/dev/null", + searchPath=1) + if rc: + raise FSError("label failed") + + self.fslabel = label + self.notifyKernel() + + @property + def isDirty(self): + return False + + @property + def mkfsProg(self): + """ Program used to create filesystems of this type. """ + return self._mkfs + + @property + def resizefsProg(self): + """ Program used to resize filesystems of this type. """ + return self._resizefs + + @property + def labelfsProg(self): + """ Program used to manage labels for this filesystem type. """ + return self._labelfs + + def supported(self): + # we aren't checking for fsck because we shouldn't need it + for prog in [self.mkfsProg, self.resizefsProg, self.labelfsProg]: + if not prog: + continue + + if not filter(lambda d: os.access("%s/%s" % (d, prog), os.X_OK), + os.environ["PATH"].split(":")): + return False + + return True + + @property + def mountable(self): + return self.type in kernel_filesystems + + @property + def defaultFormatOptions(self): + """ Default options passed to mkfs for this filesystem type. """ + # return a copy to prevent modification + return self._defaultFormatOptions[:] + + @property + def defaultMountOptions(self): + """ Default options passed to mount for this filesystem type. """ + # return a copy to prevent modification + return self._defaultMountOptions[:] + + @property + def defaultLabelOptions(self): + """ Default options passed to labeler for this filesystem type. """ + # return a copy to prevent modification + return self._defaultLabelOptions[:] + + @property + def defaultCheckOptions(self): + """ Default options passed to checker for this filesystem type. """ + # return a copy to prevent modification + return self._defaultCheckOptions[:] + + """ These methods just wrap filesystem-specific methods in more + generically named methods so filesystems and formatted devices + like swap and LVM physical volumes can have a common API. + """ + def create(self, *args, **kwargs): + if self.exists: + raise FSError("filesystem already exists") + + return self.format(*args, **kwargs) + + def setup(self, *args, **kwargs): + """ Mount the filesystem. + + THe filesystem will be mounted at the directory indicated by + self.mountpoint. + """ + return self.mount(**kwargs) + + def teardown(self, *args, **kwargs): + return self.unmount(*args, **kwargs) + + @property + def status(self): + # FIXME check /proc/mounts or similar + if not self.exists: + return False + return self._mountpoint is not None + + +class Ext2FS(FS): + """ ext2 filesystem. """ + _type = "ext2" + _mkfs = "mke2fs" + _resizefs = "resize2fs" + _labelfs = "e2label" + _fsck = "e2fsck" + _packages = ["e2fsprogs"] + _formattable = True + _supported = True + _resizable = True + _bootable = True + _linuxNative = True + _maxsize = 8 * 1024 * 1024 + _minsize = 0 + _defaultFormatOptions = [] + _defaultMountOptions = ["defaults"] + _defaultCheckOptions = ["-f", "-p", "-C", "0"] + _dump = True + _check = True + + def getMinimumSize(self): + if not self.exists: + raise FSError("filesystem has not been created") + + if not os.path.exists(self.device): + raise FSError("device does not exist") + + buf = iutil.execWithCapture(self.resizefsProg, + ["-P", self.device], + stderr="/dev/null") + minSize = None + for line in buf.splitlines(): + if "minimum size of the filesystem:" not in line: + continue + + (text, sep, minSize) = line.partition(": ") + minSize = int(minSize) + + if minSize is None: + raise FSError("failed to get minimum fs size") + + return minSize + + @property + def isDirty(self): + return isys.ext2IsDirty(self.device) + +register_device_format(Ext2FS) + + +class Ext3FS(Ext2FS): + """ ext3 filesystem. """ + _type = "ext3" + _defaultFormatOptions = ["-t", "ext3"] + +register_device_format(Ext3FS) + + +class Ext4FS(Ext3FS): + """ ext4 filesystem. """ + _type = "ext4" + _bootable = False + _defaultFormatOptions = ["-t", "ext4"] + +register_device_format(Ext4FS) + + +class FATFS(FS): + """ FAT filesystem. + + XXX Do we want to subclass this for EFI or twiddle bootable based + on the platform? + """ + _type = "vfat" + _mkfs = "mkdosfs" + _labelfs = "dosfslabel" + _fsck = "dosfsck" + _formattable = True + _maxsize = 1024 * 1024 + _packages = [ "dosfstools" ] + _defaultMountOptions = ["umask=0077", "shortname=winnt"] + + @property + def bootable(self): + retval = self._bootable + #if self.type in platform.bootableFSTypes: + # retval = True + + return retval + +register_device_format(FATFS) + + +class BTRFS(FS): + """ btrfs filesystem """ + _type = "btrfs" + _mkfs = "mkfs.btrfs" + _resizefs = "btrfsctl" + _formattable = True + _linuxNative = True + _bootable = False + _maxLabelChars = 256 + _supported = False + _dump = True + _check = True + _packages = ["btrfs-progs"] + _maxsize = 16 * 1024 * 1024 + + def _getFormatArgs(self, options=None): + argv = [] + argv.extend(options) + argv.extend(self.defaultFormatArgs) + if self.fslabel: + argv.extend(["-L", self.fslabel]) + argv.append(self.device) + return argv + + def _getResizeArgs(self): + argv = ["-r", self.targetSize, self.device] + return argv + +register_device_format(BTRFS) + + +class GFS2(FS): + """ gfs2 filesystem. """ + _type = "gfs2" + _mkfs = "mkfs.gfs2" + _formattable = True + _defaultFormatOptions = ["-j", "1", "-p", "lock_nolock", "-O"] + _linuxNative = True + _supported = False + _dump = True + _check = True + _packages = ["gfs2-utils"] + + @property + def supported(self): + """ Is this filesystem a supported type? """ + supported = self._supported + if flags.cmdline.has_key("gfs2"): + supported = True + + return supported + +register_device_format(GFS2) + + +class JFS(FS): + """ JFS filesystem """ + _type = "jfs" + _mkfs = "mkfs.jfs" + _labelfs = "jfs_tune" + _defaultFormatOptions = ["-q"] + _defaultLabelOptions = ["-L"] + _maxLabelChars = 16 + _maxsize = 8 * 1024 * 1024 + _formattable = True + _linuxNative = True + _supported = False + _dump = True + _check = True + +register_device_format(JFS) + + +class XFS(FS): + """ XFS filesystem """ + _type = "xfs" + _mkfs = "mkfs.xfs" + _labelfs = "xfs_admin" + _defaultFormatOptions = ["-f"] + _defaultLabelOptions = ["-L"] + _maxLabelChars = 16 + _maxsize = 16 * 1024 * 1024 + _formattable = True + _linuxNative = True + _supported = False + _dump = True + _check = True + _packages = ["xfsprogs"] + +register_device_format(XFS) + + +class HFS(FS): + _type = "hfs" + _mkfs = "hformat" + _formattable = True + +register_device_format(HFS) + + +# this doesn't need to be here +class HFSPlus(FS): + _type = "hfs+" + _udevTypes = ["hfsplus"] + +register_device_format(HFSPlus) + + +class NTFS(FS): + """ ntfs filesystem. """ + _type = "ntfs" + _resizefs = "ntfsresize" + _resizable = True + _defaultMountOptions = "defaults" + #_packages = ["ntfsprogs"] + + def getMinimumSize(self): + """Return the minimum filesystem size in megabytes""" + if not self.exists: + raise FSError("filesystem has not been created") + + buf = iutil.execWithCapture(self.resizefsProg, + ["-m", self.device], + stderr = "/dev/tty5") + for l in buf.split("\n"): + if not l.startswith("Minsize"): + continue + try: + min = l.split(":")[1].strip() + return int(min) + 250 + except Exception, e: + log.warning("Unable to parse output for minimum size on %s: %s" %(self.device, e)) + + log.warning("Unable to discover minimum size of filesystem on %s" %(self.device,)) + return 1 + +register_device_format(NTFS) + + +# if this isn't going to be mountable it might as well not be here +class NFS(FS): + """ NFS filesystem. """ + _type = "nfs" + + def _deviceCheck(self, devspec): + if not ":" in devspec: + raise ValueError("device must be of the form :") + + @property + def mountable(self): + return False + +register_device_format(NFS) + + +class NFSv4(NFS): + """ NFSv4 filesystem. """ + _type = "nfs4" + +register_device_format(NFSv4) + +class NoDevFS(FS): + """ nodev filesystem base class """ + _type = "nodev" + + def __init__(self, *args, **kwargs): + FS.__init__(self, *args, **kwargs) + if self.nodev and not self.device: + self.device = self.type + + def _deviceCheck(self, devspec): + pass + +class DevPtsFS(NoDevFS): + """ devpts filesystem. """ + _type = "devpts" + _defaultMountOptions = ["gid=5", "mode=620"] + +register_device_format(DevPtsFS) + + +# these don't really need to be here +class ProcFS(NoDevFS): + _type = "proc" + +register_device_format(ProcFS) + + +class SysFS(NoDevFS): + _type = "sysfs" + +register_device_format(SysFS) + + +class TmpFS(NoDevFS): + _type = "tmpfs" + +register_device_format(TmpFS) + + +class BindFS(FS): + _type = "bind" + +register_device_format(BindFS) + + diff --git a/storage/formats/luks.py b/storage/formats/luks.py new file mode 100644 index 000000000..d995fa68e --- /dev/null +++ b/storage/formats/luks.py @@ -0,0 +1,223 @@ +# luks.py +# Device format classes for anaconda's storage configuration module. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# + + + +import os + +from errors import * +from iutil import log_method_call +from deviceformat import * +import crypto + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("storage") + + +class LUKS(DeviceFormat): + """ A LUKS device. """ + _type = "luks" + _name "LUKS" + _udevTypes = ["crypto_LUKS"] + _formattable = True # can be formatted + _supported = True # is supported + _linuxNative = True # for clearpart + _packages = ["cryptsetup-luks"] # required packages + + def __init__(self, *args, **kwargs): + """ Create a LUKS instance. + + Keyword Arguments: + + device -- the path to the underlying device + name -- the name of the mapped device + uuid -- this device's UUID + passphrase -- device passphrase (string) + key_file -- path to a file containing a key (string) + cipher -- cipher mode string + key_size -- key size in bits + exists -- indicates whether this is an existing format + """ + log_method_call(self, *args, **kwargs) + DeviceFormat.__init__(self, *args, **kwargs) + self.cipher = kwargs.get("cipher") + self.key_size = kwargs.get("key_size") + self.mapName = kwargs.get("name") + + # FIXME: these should both be lists, but managing them will be a pain + self.__passphrase = kwargs.get("passphrase") + self._key_file = kwargs.get("key_file") + + if not self.mapName: + self.mapName = "luks-%s" % os.path.basename(self.device) + + def _setPassphrase(self, passphrase): + """ Set the passphrase used to access this device. """ + self.__passphrase = passphrase + + passphrase = property(fset=_setPassphrase) + + @property + def configured(self): + """ To be ready we need a key or passphrase and a map name. """ + ready = True + if not self.__passphrase or \ + (self._key_file and os.access(self._key_file, os.R_OK)): + ready = False + + if not self.mapName: + ready = False + + return ready + + @property + def status(self): + if not self.exists or not self.mapName: + return False + return os.path.exists("/dev/mapper/%s" % self.mapName) + + def probe(self): + """ Probe for any missing information about this format. + + cipher mode, key size + """ + raise NotImplementedError("probe method not defined for LUKS") + + def setup(self, *args, **kwargs): + """ Open, or set up, the format. """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + if not self.exists: + raise LUKSError("format has not been created") + + if not self.configured: + raise LUKSError("luks device not configured") + + if self.status: + return + + crypto.luks_open(self.device, self.mapName, + passphrase=self.__passphrase, + key_file=self._key_file) + + def teardown(self, *args, **kwargs): + """ Close, or tear down, the format. """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + if not self.exists: + raise LUKSError("format has not been created") + + if self.status: + log.debug("unmapping %s" % self.mapName) + crypto.luks_close(self.mapName) + + def create(self, *args, **kwargs): + """ Create the format. """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + if self.exists: + raise LUKSError("format already exists") + + if not self.configured: + raise LUKSError("luks device not configured") + + crypto.luks_format(self.device, + passphrase=self.__passphrase, + key_file=self._key_file, + cipher=self.cipher, + key_size=self.key_size) + + self.uuid = crypto.luks_uuid(self.device) + self.notifyKernel() + + @property + def keyFile(self): + """ Path to key file to be used in /etc/crypttab """ + return self._key_file + + def addKeyFromFile(self, keyfile): + """ Add a new key from a file. + + Add the contents of the specified key file to an available key + slot in the LUKS header. + """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status, file=keyfile) + if not self.exists: + raise LUKSError("format has not been created") + + crypto.luks_add_key(self.device, + passphrase=self.__passphrase, + key_file=self._key_file, + new_key_file=keyfile) + + def addPassphrase(self, passphrase): + """ Add a new passphrase. + + Add the specified passphrase to an available key slot in the + LUKS header. + """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + if not self.exists: + raise LUKSError("format has not been created") + + crypto.luks_add_key(self.device, + passphrase=self.__passphrase, + key_file=self._key_file, + new_passphrase=passphrase) + + def removeKeyFromFile(self, keyfile): + """ Remove a key contained in a file. + + Remove key contained in the specified key file from the LUKS + header. + """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status, file=keyfile) + if not self.exists: + raise LUKSError("format has not been created") + + crypto.luks_remove_key(self.device, + passphrase=self.__passphrase, + key_file=self._key_file, + del_key_file=keyfile) + + + def removePassphrase(self, passphrase): + """ Remove the specified passphrase from the LUKS header. """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + if not self.exists: + raise LUKSError("format has not been created") + + crypto.luks_remove_key(self.device, + passphrase=self.__passphrase, + key_file=self._key_file, + del_passphrase=passphrase) + + +register_device_format(LUKS) + diff --git a/storage/formats/lvmpv.py b/storage/formats/lvmpv.py new file mode 100644 index 000000000..9747eb1b6 --- /dev/null +++ b/storage/formats/lvmpv.py @@ -0,0 +1,117 @@ +# lvmpv.py +# Device format classes for anaconda's storage configuration module. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# + +from errors import * +from iutil import log_method_call +from deviceformat import * +import lvm +#from parted import PARTITION_LVM + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("storage") + + +class LVMPhysicalVolume(DeviceFormat): + """ An LVM physical volume. """ + _type = "lvmpv" + _name = "physical volume (LVM)" + _udevTypes = ["LVM2_member"] + #partedFlags = PARTITION_LVM + _formattable = True # can be formatted + _supported = True # is supported + _linuxNative = True # for clearpart + _packages = ["lvm2"] # required packages + + def __init__(self, *args, **kwargs): + """ Create an LVMPhysicalVolume instance. + + Keyword Arguments: + + device -- path to the underlying device + uuid -- this PV's uuid (not the VG uuid) + vgName -- the name of the VG this PV belongs to + vgUuid -- the UUID of the VG this PV belongs to + peStart -- offset of first physical extent + exists -- indicates whether this is an existing format + + """ + log_method_call(self, *args, **kwargs) + DeviceFormat.__init__(self, *args, **kwargs) + self.vgName = kwargs.get("vgName") + self.vgUuid = kwargs.get("vgUuid") + # liblvm may be able to tell us this at some point, even + # for not-yet-created devices + self.peStart = kwargs.get("peStart", 0.1875) # in MB + + def probe(self): + """ Probe for any missing information about this device. """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + if not self.exists: + raise PhysicalVolumeError("format has not been created") + + pass + #info = lvm.pvinfo(self.device) + #self.vgName = info['vg_name'] + #self.vgUuid = info['vg_uuid'] + + def create(self, *args, **kwargs): + """ Create the format. """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + if self.exists: + raise PhysicalVolumeError("format already exists") + + """ Consider use of -Z|--zero + -f|--force or -y|--yes may be required + """ + lvm.pvcreate(self.device) + self.notifyKernel() + + def destroy(self, *args, **kwargs): + """ Destroy the format. """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + if not self.exists: + raise PhysicalVolumeError("format has not been created") + + if self.status: + raise PhysicalVolumeError("device is active") + + # FIXME: verify path exists? + lvm.pvremove(self.device) + self.notifyKernel() + + @property + def status(self): + # XXX hack + for d in os.listdir("/dev/mapper"): + if d.startswith("%s-" % self.vgName): + return True + return False + + +register_device_format(LVMPhysicalVolume) + diff --git a/storage/formats/mdraid.py b/storage/formats/mdraid.py new file mode 100644 index 000000000..0975269b1 --- /dev/null +++ b/storage/formats/mdraid.py @@ -0,0 +1,96 @@ +# mdraid.py +# Device format classes for anaconda's storage configuration module. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# + +import os + +from errors import * +from iutil import log_method_call +from deviceformat import * +import mdraid +#from parted import PARTITION_RAID + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("storage") + + +class MDRaidMember(DeviceFormat): + """ An mdraid member disk. """ + _type = "mdmember" + _name = "software RAID" + _udevTypes = ["linux_raid_member"] + #partedFlags = PARTITION_RAID + _formattable = True # can be formatted + _supported = True # is supported + _linuxNative = True # for clearpart + _packages = ["mdadm"] # required packages + + def __init__(self, *args, **kwargs): + """ Create a MDRaidMember instance. + + Keyword Arguments: + + device -- path to underlying device + uuid -- this member device's uuid + mdUuid -- the uuid of the array this device belongs to + exists -- indicates whether this is an existing format + + """ + log_method_call(self, *args, **kwargs) + DeviceFormat.__init__(self, *args, **kwargs) + self.mdUuid = kwargs.get("mdUuid") + self.raidMinor = None + + #self.probe() + + def probe(self): + """ Probe for any missing information about this format. """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + if not self.exists: + raise MDRaidMemberError("format does not exist") + + info = mdraid.mdexamine(self.device) + if self.uuid is None: + self.uuid = info['uuid'] + if self.raidMinor is None: + self.raidMinor = info['mdMinor'] + + def destroy(self, *args, **kwargs): + if not self.exists: + raise MDMemberError("format does not exist") + + if not os.path.access(self.device, os.W_OK): + raise MDMemberError("device path does not exist") + + mdadm.mddestroy(self.device) + + @property + def status(self): + # XXX hack -- we don't have a nice way to see if the array is active + return False + + +register_device_format(MDRaidMember) + diff --git a/storage/formats/swap.py b/storage/formats/swap.py new file mode 100644 index 000000000..763921503 --- /dev/null +++ b/storage/formats/swap.py @@ -0,0 +1,137 @@ +# swap.py +# Device format classes for anaconda's storage configuration module. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# + +import os + +from errors import * +from iutil import log_method_call +from deviceformat import * +import swap +#from parted import PARTITION_SWAP + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("storage") + + +class SwapSpace(DeviceFormat): + """ Swap space """ + _type = "swap" + _udevTypes = ["swap"] + #partedFlags = PARTITION_SWAP + _formattable = True # can be formatted + _supported = True # is supported + _linuxNative = True # for clearpart + + def __init__(self, *args, **kwargs): + """ Create a SwapSpace instance. + + Keyword Arguments: + + device -- path to the underlying device + uuid -- this swap space's uuid + label -- this swap space's label + priority -- this swap space's priority + exists -- indicates whether this is an existing format + + """ + log_method_call(self, *args, **kwargs) + DeviceFormat.__init__(self, *args, **kwargs) + + self.priority = kwargs.get("priority") + self.label = kwargs.get("label") + + def _setPriority(self, priority): + if priority is None: + self._priority = None + return + + if not isinstance(priority, int) or not 0 <= priority <= 32767: + raise ValueError("swap priority must be an integer between 0 and 32767") + + self._priority = priority + + def _getPriority(self): + return self._priority + + priority = property(_getPriority, _setPriority, + doc="The priority of the swap device") + + def _getOptions(self): + opts = "" + if self.priority is not None: + opts += "pri=%d" % self.priority + + return opts + + def _setOptions(self, opts): + for (opt, arg) in opts.split(","): + if opt == "pri": + try: + + options = property(_getOptions, _setOptions, + doc="The swap device's fstab options string") + + @property + def status(self): + """ Device status. """ + return self.exists and swap.swapstatus(self.device) + + def setup(self, *args, **kwargs): + """ Open, or set up, a device. """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + if not self.exists: + raise SwapSpaceError("format has not been created") + + if self.status: + return + + swap.swapon(self.device, priority=self.priority) + + def teardown(self, *args, **kwargs): + """ Close, or tear down, a device. """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + if not self.exists: + raise SwapSpaceError("format has not been created") + + if self.status: + swap.swapoff(self.device) + + def create(self, *args, **kwargs): + """ Create the device. """ + log_method_call(self, device=os.path.basename(self.device), + type=self.type, status=self.status) + if self.exists: + raise SwapSpaceError("format already exists") + + if self.status: + raise SwapSpaceError("device exists and is active") + + swap.mkswap(self.device, label=self.label) + + +register_device_format(SwapSpace) + diff --git a/storage/iscsi.py b/storage/iscsi.py new file mode 100644 index 000000000..214a9c877 --- /dev/null +++ b/storage/iscsi.py @@ -0,0 +1,332 @@ +# +# iscsi.py - iscsi class +# +# Copyright (C) 2005, 2006 IBM, Inc. All rights reserved. +# Copyright (C) 2006 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +from constants import * +import os +import errno +import string +import signal +import iutil +import isys +from flags import flags +import logging +import shutil +import time +import md5, random +import partedUtils +log = logging.getLogger("anaconda") + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +has_libiscsi = True +try: + import libiscsi +except: + has_libiscsi = False + +# Note that stage2 copies all files under /sbin to /usr/sbin +global ISCSID +ISCSID="" +global ISCSIADM +ISCSIADM = "" +INITIATOR_FILE="/etc/iscsi/initiatorname.iscsi" + +def find_iscsi_files(): + global ISCSID + if ISCSID == "": + for dir in ("/usr/sbin", "/tmp/updates", "/mnt/source/RHupdates"): + path="%s/iscsid" % (dir,) + if os.access(path, os.X_OK): + ISCSID=path + global ISCSIADM + if ISCSIADM == "": + for dir in ("/usr/sbin", "/tmp/updates", "/mnt/source/RHupdates"): + path="%s/iscsiadm" % (dir,) + if os.access(path, os.X_OK): + ISCSIADM=path + +def has_iscsi(): + find_iscsi_files() + if ISCSID == "" or ISCSIADM == "" or not has_libiscsi: + return False + + log.info("ISCSID is %s" % (ISCSID,)) + log.info("ISCSIADM is %s" % (ISCSIADM,)) + + # make sure the module is loaded + if not os.access("/sys/module/iscsi_tcp", os.X_OK): + return False + return True + +def iscsi_get_node_record(node_settings, record): + for line in node_settings: + if line.startswith(record): + words = line.split(" = ") + if len(words) == 2: + return words[1] + # should never happen but better safe then sorry + break + + return None + +# FIXME replace with libiscsi use +def iscsi_make_node_autostart(disk): + sysfs_path = os.path.realpath("/sys/block/%s/device" %(disk,)) + argv = [ "-m", "session", "-r", sysfs_path ] + log.debug("iscsiadm %s" %(string.join(argv),)) + node_settings = iutil.execWithCapture(ISCSIADM, argv, stderr="/dev/tty5").splitlines() + node_name = iscsi_get_node_record(node_settings, "node.name") + argv = [ "-m", "node", "-T", node_name, "-o", "update", "-n", + "node.startup", "-v", "automatic" ] + log.debug("iscsiadm %s" %(string.join(argv),)) + iutil.execWithRedirect(ISCSIADM, argv, + stdout = "/dev/tty5", stderr="/dev/tty5") + +def randomIname(): + """Generate a random initiator name the same way as iscsi-iname""" + + s = "iqn.1994-05.com.fedora:01." + m = md5.md5() + u = os.uname() + for i in u: + m.update(i) + dig = m.hexdigest() + + for i in range(0, 6): + s += dig[random.randrange(0, 32)] + return s + +def stabilize(intf = None): + # Wait for udev to create the devices for the just added disks + if intf: + w = intf.waitWindow(_("Scanning iSCSI nodes"), + _("Scanning iSCSI nodes")) + # It is possible when we get here the events for the new devices + # are not send yet, so sleep to make sure the events are fired + time.sleep(2) + iutil.execWithRedirect("udevadm", [ "settle" ], + stdout = "/dev/tty5", stderr="/dev/tty5", + searchPath = 1) + if intf: + w.pop() + +class iscsi(object): + def __init__(self): + self.nodes = [] + self._initiator = "" + self.initiatorSet = False + self.started = False + + try: + initiatorname = libiscsi.get_firmware_initiator_name() + self._initiator = initiatorname + self.initiatorSet = True + except: + pass + + def _getInitiator(self): + if self._initiator != "": + return self._initiator + + return randomIname() + + def _setInitiator(self, val): + if self._initiator != "" and val != self._initiator: + raise ValueError, "Unable to change iSCSI initiator name once set" + if len(val) == 0: + raise ValueError, "Must provide a non-zero length string" + self._initiator = val + self.initiatorSet = True + + initiator = property(_getInitiator, _setInitiator) + + def _startIBFT(self, intf = None): + if not flags.ibft: + return + + try: + found_nodes = libiscsi.discover_firmware() + except: + # an exception here means there is no ibft firmware, just return + return + + for node in found_nodes: + try: + node.login() + self.nodes.append(node) + except: + # FIXME, what to do when we cannot log in to a firmware + # provided node ?? + pass + + stabilize(intf) + + def startup(self, intf = None): + if self.started: + return + + if not has_iscsi(): + return + + if not self.initiatorSet: + log.info("no initiator set") + return + + if intf: + w = intf.waitWindow(_("Initializing iSCSI initiator"), + _("Initializing iSCSI initiator")) + + log.debug("Setting up %s" % (INITIATOR_FILE, )) + log.info("iSCSI initiator name %s", self.initiator) + if os.path.exists(INITIATOR_FILE): + os.unlink(INITIATOR_FILE) + if not os.path.isdir("/etc/iscsi"): + os.makedirs("/etc/iscsi", 0755) + fd = os.open(INITIATOR_FILE, os.O_RDWR | os.O_CREAT) + os.write(fd, "InitiatorName=%s\n" %(self.initiator)) + os.close(fd) + + for dir in ['ifaces','isns','nodes','send_targets','slp','static']: + fulldir = "/var/lib/iscsi/%s" % (dir,) + if not os.path.isdir(fulldir): + os.makedirs(fulldir, 0755) + + log.info("iSCSI startup") + iutil.execWithRedirect(ISCSID, [], + stdout="/dev/tty5", stderr="/dev/tty5") + time.sleep(1) + + if intf: + w.pop() + + self._startIBFT(intf) + self.started = True + + def addTarget(self, ipaddr, port="3260", user=None, pw=None, + user_in=None, pw_in=None, intf=None): + authinfo = None + found = 0 + logged_in = 0 + + if not has_iscsi(): + raise IOError, _("iSCSI not available") + if not self.initiatorSet: + raise ValueError, _("No initiator name set") + + self.startup(intf) + + if user: + # Note may raise a ValueError + authinfo = libiscsi.chapAuthInfo(username=user, password=pw, + reverse_username=user_in, + reverse_password=pw_in) + # Note may raise an IOError + found_nodes = libiscsi.discover_sendtargets(address=ipaddr, + port=int(port), + authinfo=authinfo) + if found_nodes == None: + raise IOError, _("No iSCSI nodes discovered") + + if intf: + w = intf.waitWindow(_("Logging in to iSCSI nodes"), + _("Logging in to iSCSI nodes")) + + for node in found_nodes: + # skip nodes we already have + if node in self.nodes: + continue + + found = found + 1 + try: + if (authinfo): + node.setAuth(authinfo) + node.login() + self.nodes.append(node) + logged_in = logged_in + 1 + except: + # some nodes may require different credentials + pass + + if intf: + w.pop() + + if found == 0: + raise IOError, _("No new iSCSI nodes discovered") + + if logged_in == 0: + raise IOError, _("Could not log in to any of the discovered nodes") + + stabilize(intf) + + def writeKS(self, f): + if not self.initiatorSet: + return + f.write("iscsiname %s\n" %(self.initiator,)) + for n in self.nodes: + f.write("iscsi --ipaddr %s --port %s" %(n.address, n.port)) + auth = n.getAuth() + if auth: + f.write(" --user %s" %(n.username,)) + f.write(" --password %s" %(n.password,)) + if len(auth.reverse_username): + f.write(" --reverse-user %s" % (n.reverse_username,)) + f.write(" --reverse-password %s" % (n.reverse_password,)) + f.write("\n") + + def write(self, instPath, anaconda): + if not self.initiatorSet: + return + + if not flags.test: + root_drives = [ ] + req = anaconda.id.partitions.getRequestByMountPoint("/") + root_requests = anaconda.id.partitions.getUnderlyingRequests(req) + for req in root_requests: + for drive in req.drive: + part = anaconda.id.diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device) + if part: + break + if not part: + continue + if drive not in root_drives: + root_drives.append(drive) + + log.debug("iscsi.write: root_drives: %s" % (string.join(root_drives),)) + + # set iscsi nodes not used for root to autostart + for disk in anaconda.id.diskset.disks.keys(): + if isys.driveIsIscsi(disk) and not disk in root_drives: + iscsi_make_node_autostart(disk) + + if not os.path.isdir(instPath + "/etc/iscsi"): + os.makedirs(instPath + "/etc/iscsi", 0755) + fd = os.open(instPath + INITIATOR_FILE, os.O_RDWR | os.O_CREAT) + os.write(fd, "InitiatorName=%s\n" %(self.initiator)) + os.close(fd) + + # copy "db" files. *sigh* + if os.path.isdir(instPath + "/var/lib/iscsi"): + shutil.rmtree(instPath + "/var/lib/iscsi") + if os.path.isdir("/var/lib/iscsi"): + shutil.copytree("/var/lib/iscsi", instPath + "/var/lib/iscsi", + symlinks=True) + +# vim:tw=78:ts=4:et:sw=4 diff --git a/storage/miscutils.py b/storage/miscutils.py new file mode 100644 index 000000000..b9c9740f1 --- /dev/null +++ b/storage/miscutils.py @@ -0,0 +1,58 @@ +# iutil.py stubs +import sys +import os + +import logging +log = logging.getLogger("storage") + +def notify_kernel(path, action="change"): + """ Signal the kernel that the specified device has changed. """ + log.debug("notifying kernel of '%s' event on device %s" % (action, path)) + path = os.path.join(path, "uevent") + if not path.startswith("/sys/") or not os.access(path, os.W_OK): + log.debug("sysfs path '%s' invalid" % path) + raise ValueError("invalid sysfs path") + + f = open(path, "a") + f.write("%s\n" % action) + f.close() + +def get_sysfs_path_by_name(dev_name, class_name="block"): + dev_name = os.path.basename(dev_name) + sysfs_class_dir = "/sys/class/%s" % class_name + dev_path = os.path.join(sysfs_class_dir, dev_name) + if os.path.exists(dev_path): + return dev_path + +import inspect +def log_method_call(d, *args, **kwargs): + classname = d.__class__.__name__ + methodname = inspect.stack()[1][3] + fmt = "%s.%s:" + fmt_args = [classname, methodname] + for arg in args: + fmt += " %s ;" + fmt_args.append(arg) + + for k, v in kwargs.items(): + fmt += " %s: %s ;" + fmt_args.extend([k, v]) + + log.debug(fmt % tuple(fmt_args)) + +def numeric_type(num): + """ Verify that a value is given as a numeric data type. + + Return the number if the type is sensible or raise ValueError + if not. + """ + if num is None: + num = 0 + elif not (isinstance(num, int) or \ + isinstance(num, long) or \ + isinstance(num, float)): + raise ValueError("value (%s) must be either a number or None" % num) + + return num + + diff --git a/storage/partitioning.py b/storage/partitioning.py new file mode 100644 index 000000000..ac7e5da53 --- /dev/null +++ b/storage/partitioning.py @@ -0,0 +1,735 @@ +# partitioning.py +# Disk partitioning functions. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# + +import os +import copy +from operator import add, sub + +# XXX temporary +import sys +sys.path.insert(0, "/root/pyparted/src") +sys.path.insert(1, "/root/pyparted/src/.libs") +import parted + +from errors import * +from deviceaction import * +from pykickstart.constants import * + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("storage") + + +def clearPartitions(storage, clearPartType=CLEARPART_TYPE_NONE, + clearPartDisks=[]): + """ Clear partitions and dependent devices from disks. + + Arguments: + + deviceTree -- a DeviceTree instance + + Keyword arguments: + + clearPartType -- a pykickstart CLEARPART_TYPE_* constant + clearPartDisks -- a list of basenames of disks to consider + + NOTES: + + - Needs some error handling, especially for the parted bits. + - Should use device actions instead of dev.destroy, &c + + """ + if not clearPartType or clearPartType == CLEARPART_TYPE_NONE: + # not much to do -- just remove any empty extended partitions + removeEmptyExtendedPartitions(deviceTree) + return + + # we are only interested in partitions that physically exist + partitions = [p for p in storage.partitions if p.exists] + disks = [] # a list of disks from which we've removed partitions + + for part in partitions: + log.debug("clearpart: looking at %s" % part.name) + clear = False # whether or not we will clear this partition + + # if we got a list of disks to clear, make sure this one's on it + if clearPartDisks and not part.disk.name in clearPartDisks: + continue + + # we don't want to fool with extended partitions, freespace, &c + if part.partType not in (parted.PARTITION_NORMAL, + parted.PARTITION_LOGICAL): + continue + + if clearPartType == CLEARPART_TYPE_ALL: + clear = True + else: + if part.format and part.format.linuxNative: + clear = True + elif part.partedPartition.getFlag(parted.PARTITION_LVM) or \ + part.partedPartition.getFlag(parted.PARTITION_RAID) or \ + part.partedPartition.getFlag(parted.PARTITION_SWAP): + clear = True + + # TODO: do platform-specific checks on ia64, pSeries, iSeries, mac + + if not clear: + continue + + log.debug("clearing %s" % part.name) + + # XXX is there any argument for not removing incomplete devices? + # -- maybe some RAID devices + devices = storage.deviceDeps(part) + while devices: + log.debug("devices to remove: %s" % ([d.name for d in devices],)) + leaves = [d for d in devices if d.isleaf] + log.debug("leaves to remove: %s" % ([d.name for d in leaves],)) + for leaf in leaves: + action = ActionDestroyDevice(leaf) + deviceTree.registerAction(action) + devices.remove(leaf) + + # XXX can/should this be moved into PartitionDevice? + part.partedPartition.disk.removePartition(part.partedPartition) + log.debug("partitions: %s" % [p.getDeviceNodeName() for p in part.partedPartition.disk.partitions]) + disk_name = os.path.basename(part.partedPartition.disk.device.path) + if disk_name not in disks: + disks.append(disk_name) + + action = ActionDestroyDevice(part) + deviceTree.registerAction(action) + + # now remove any empty extended partitions + removeEmptyExtendedPartitions(deviceTree) + + +def removeEmptyExtendedPartitions(storage): + for disk in storage.disks: + log.debug("checking whether disk %s has an empty extended" % disk.name) + extended = disk.partedDisk.getExtendedPartition() + logical_parts = disk.partedDisk.getLogicalPartitions() + log.debug("extended is %s ; logicals is %s" % (extended, [p.getDeviceNodeName() for p in logical_parts])) + if extended and not logical_parts: + log.debug("removing empty extended partition from %s" % disk.name) + extended_name = extended.getDeviceNodeName() + extended = storage.devicetree.getDeviceByName(extended_name) + storage.devicetree.registerAction(ActionDestroyDevice(extended)) + #disk.partedDisk.removePartition(extended.partedPartition) + + +def partitionCompare(part1, part2): + """ More specifically defined partitions come first. + + < 1 => x < y + 0 => x == y + > 1 => x > y + """ + ret = 0 + + # bootable partitions to the front + ret -= cmp(part1.req_bootable, part2.req_bootable) * 1000 + + # more specific disk specs to the front of the list + ret += cmp(len(part1.parents), len(part2.parents)) * 500 + + # primary-only to the front of the list + ret -= cmp(part1.req_primary, part2.req_primary) * 200 + + # larger requests go to the front of the list + ret -= cmp(part1.size, part2.size) * 100 + + # fixed size requests to the front + ret += cmp(part1.req_grow, part2.req_grow) * 50 + + # potentially larger growable requests go to the front + if part1.req_grow and part2.req_grow: + if not part1.req_max_size and part2.req_max_size: + ret -= 25 + elif part1.req_max_size and not part2.req_max_size: + ret += 25 + else: + ret -= cmp(part1.req_max_size, part2.req_max_size) * 25 + + if ret > 0: + ret = 1 + elif ret < 0: + ret = -1 + + return ret + +def getNextPartitionType(disk, no_primary=None): + """ Find the type of partition to create next on a disk. + + Return a parted partition type value representing the type of the + next partition we will create on this disk. + + If there is only one free primary partition and we can create an + extended partition, we do that. + + If there are free primary slots and an extended partition we will + recommend creating a primary partition. This can be overridden + with the keyword argument no_primary. + + Arguments: + + disk -- a parted.Disk instance representing the disk + + Keyword arguments: + + no_primary -- given a choice between primary and logical + partitions, prefer logical + + """ + part_type = None + extended = disk.getExtendedPartition() + supports_extended = disk.supportsFeature(parted.DISK_TYPE_EXTENDED) + logical_count = len(disk.getLogicalPartitions()) + max_logicals = disk.getMaxLogicalPartitions() + primary_count = disk.primaryPartitionCount + + if primary_count == disk.maxPrimaryPartitionCount and \ + extended and logical_count < max_logicals: + part_type = parted.PARTITION_LOGICAL + elif primary_count == (disk.maxPrimaryPartitionCount - 1) and \ + not extended and supports_extended: + # last chance to create an extended partition + part_type = parted.PARTITION_EXTENDED + elif no_primary and extended and logical_count < max_logicals: + # create a logical even though we could presumably create a + # primary instead + part_type = parted.PARTITION_LOGICAL + elif not no_primary: + # XXX there is a possiblity that the only remaining free space on + # the disk lies within the extended partition, but we will + # try to create a primary first + part_type = parted.PARTITION_NORMAL + + return part_type + +def getBestFreeSpaceRegion(disk, part_type, req_size, + boot=None, best_free=None): + """ Return the "best" free region on the specified disk. + + For non-boot partitions, we return the largest free region on the + disk. For boot partitions, we return the first region that is + large enough to hold the partition. + + Partition type (parted's PARTITION_NORMAL, PARTITION_LOGICAL) is + taken into account when locating a suitable free region. + + For locating the best region from among several disks, the keyword + argument best_free allows the specification of a current "best" + free region with which to compare the best from this disk. The + overall best region is returned. + + Arguments: + + disk -- the disk (a parted.Disk instance) + part_type -- the type of partition we want to allocate + (one of parted's partition type constants) + req_size -- the requested size of the partition (in MB) + + Keyword arguments: + + boot -- indicates whether this will be a bootable partition + (boolean) + best_free -- current best free region for this partition + + """ + log.debug("getBestFreeSpaceRegion: disk=%s part_type=%d req_size=%dMB boot=%s best=%s" % (disk.device.path, part_type, req_size, boot, best_free)) + extended = disk.getExtendedPartition() + for _range in disk.getFreeSpaceRegions(): + if extended: + # find out if there is any overlap between this region and the + # extended partition + log.debug("looking for intersection between extended (%d-%d) and free (%d-%d)" % (extended.geometry.start, extended.geometry.end, _range.start, _range.end)) + + # parted.Geometry.overlapsWith can handle this + try: + free_geom = extended.geometry.intersect(_range) + except ArithmeticError, e: + # this freespace region does not lie within the extended + # partition's geometry + free_geom = None + + if (free_geom and part_type == parted.PARTITION_NORMAL) or \ + (not free_geom and part_type == parted.PARTITION_LOGICAL): + log.debug("free region not suitable for request") + continue + + if part_type == parted.PARTITION_NORMAL: + # we're allocating a primary and the region is not within + # the extended, so we use the original region + free_geom = _range + else: + free_geom = _range + + log.debug("current free range is %d-%d (%dMB)" % (free_geom.start, + free_geom.end, + free_geom.getSize())) + free_size = free_geom.getSize() + + if req_size <= free_size: + if not best_free or free_geom.length > best_free.length: + best_free = free_geom + + if boot: + # if this is a bootable partition we want to + # use the first freespace region large enough + # to satisfy the request + break + + return best_free + +def doPartitioning(storage, exclusiveDisks=[]): + """ Allocate and grow partitions. + + When this function returns without error, all PartitionDevice + instances must have their parents set to the disk they are + allocated on, and their partedPartition attribute set to the + appropriate parted.Partition instance from their containing + disk. All req_xxxx attributes must be unchanged. + + Arguments: + + storage -- the Storage instance + + Keyword arguments: + + exclusiveDisks -- a list of basenames of disks to use + + """ + disks = storage.disks + partitions = storage.partitions + if exclusiveDisks: + # only use specified disks + disks = [d for d in disks if d.name in exclusiveDisks] + + # FIXME: make sure non-existent partitions have empty parents list + allocatePartitions(disks, partitions) + growPartitions(disks, partitions) + + # XXX hack -- if we created any extended partitions we need to add + # them to the tree now + for disk in disks: + extended = disk.partedDisk.getExtendedPartition() + if extended.getDeviceNodeName() in [p.name for p in partitions]: + # this extended partition is preexisting + continue + + device = PartitionDevice(extended.getDeviceNodeName(), + parents=disk) + device.setPartedPartition(extended) + storage.addDevice(device) + +def allocatePartitions(disks, partitions): + """ Allocate partitions based on requested features. + + Non-existing partitions are sorted according to their requested + attributes, and then allocated. + + The basic approach to sorting is that the more specifically- + defined a request is, the earlier it will be allocated. See + the function partitionCompare for details on the sorting + criteria. + + The PartitionDevice instances will have their name and parents + attributes set once they have been allocated. + """ + log.debug("allocatePartitions: disks=%s ; partitions=%s" % (disks, + partitions)) + new_partitions = [p for p in partitions if not p.exists] + new_partitions.sort(cmp=partitionCompare) + + # XXX is this needed anymore? + partedDisks = {} + for disk in disks: + if disk.path not in partedDisks.keys(): + partedDisks[disk.path] = disk.partedDisk #.duplicate() + + # remove all newly added partitions from the disk + log.debug("removing all non-preexisting from disk(s)") + for _part in new_partitions: + if _part.partedPartition: + #_part.disk.partedDisk.removePartition(_part.partedPartition) + partedDisk = partedDisks[_part.disk.partedDisk.device.path] + #log.debug("removing part %s (%s) from disk %s (%s)" % (_part.partedPartition.path, [p.path for p in _part.partedPartition.disk.partitions], partedDisk.device.path, [p.path for p in partedDisk.partitions])) + partedDisk.removePartition(_part.partedPartition) + # remove empty extended so it doesn't interfere + extended = partedDisk.getExtendedPartition() + if extended and not partedDisk.getLogicalPartitions(): + log.debug("removing empty extended partition") + #partedDisk.minimizeExtendedPartition() + partedDisk.removePartition(extended) + + for _part in new_partitions: + # obtain the set of candidate disks + req_disks = [] + if _part.disk: + # we have a already selected a disk for this request + req_disks = [_part.disk] + elif _part.req_disks: + # use the requested disk set + req_disks = _part.req_disks + else: + # no disks specified means any disk will do + req_disks = disks + + log.debug("allocating partition: %s ; disks: %s ; boot: %s ; primary: %s ; size: %dMB ; grow: %s ; max_size: %s" % (_part.name, req_disks, _part.req_bootable, _part.req_primary, _part.req_size, _part.req_grow, _part.req_max_size)) + free = None + # loop through disks + for _disk in req_disks: + disk = partedDisks[_disk.path] + #for p in disk.partitions: + # log.debug("disk %s: part %s" % (disk.device.path, p.path)) + sectorSize = disk.device.physicalSectorSize + part_type = parted.PARTITION_NORMAL + best = None + + # TODO: On alpha we are supposed to reserve either one or two + # MB at the beginning of each disk. Awesome. + # -- maybe we do not care about alpha... + + log.debug("checking freespace on %s" % _disk.name) + + part_type = getNextPartitionType(disk) + if part_type is None: + # can't allocate any more partitions on this disk + log.debug("no free partition slots on %s" % _disk.name) + continue + + if _part.req_primary and part_type != parted.PARTITION_NORMAL: + # we need a primary slot and none are free on this disk + log.debug("no primary slots available on %s" % _disk.name) + continue + + best = getBestFreeSpaceRegion(disk, + part_type, + _part.req_size, + best_free=free, + boot=_part.req_bootable) + + if best == free and not _part.req_primary and \ + part_type == parted.PARTITION_NORMAL: + # see if we can do better with a logical partition + log.debug("not enough free space for primary -- trying logical") + part_type = getNextPartitionType(disk, no_primary=True) + if part_type: + free = getBestFreeSpaceRegion(disk, + part_type, + _part.req_size, + best_free=free, + boot=_part.req_bootable) + else: + free = best + + if free and _part.req_bootable: + # if this is a bootable partition we want to + # use the first freespace region large enough + # to satisfy the request + log.debug("found free space for bootable request") + break + + if free is None: + raise PartitioningError("not enough free space on disks") + + # create the extended partition if needed + # TODO: move to a function (disk, free) + if part_type == parted.PARTITION_EXTENDED: + log.debug("creating extended partition") + geometry = parted.Geometry(device=disk.device, + start=free.start, + length=free.length, + end=free.end) + extended = parted.Partition(disk=disk, + type=parted.PARTITION_EXTENDED, + geometry=geometry) + constraint = parted.Constraint(device=disk.device) + # FIXME: we should add this to the tree as well + disk.addPartition(extended, constraint) + + # end proposed function + + # now the extended partition exists, so set type to logical + part_type = parted.PARTITION_LOGICAL + + # recalculate freespace + log.debug("recalculating free space") + free = getBestFreeSpaceRegion(disk, + part_type, + _part.req_size, + boot=_part.req_bootable) + if not free: + raise PartitioningError("not enough free space after " + "creating extended partition") + + # create minimum geometry for this request + # req_size is in MB + length = (_part.req_size * (1024 * 1024)) / sectorSize + new_geom = parted.Geometry(device=disk.device, + start=free.start, + length=length) + + # create the partition and add it to the disk + partition = parted.Partition(disk=disk, + type=part_type, + geometry=new_geom) + disk.addPartition(partition=partition, + constraint=disk.device.getConstraint()) +# constraint=parted.Constraint(device=disk.device)) + log.debug("created partition %s of %dMB and added it to %s" % (partition.getDeviceNodeName(), partition.getSize(), disk)) + _part.setPartedPartition(partition) + _part.disk = _disk + + +def growPartitions(disks, partitions): + """ Grow all growable partition requests. + + All requests should know what disk they will be on by the time + this function is called. This is reflected in the + PartitionDevice's disk attribute. Note that the req_disks + attribute remains unchanged. + + The total available free space is summed up for each disk and + partition requests are allocated a maximum percentage of the + available free space on their disk based on their own base size. + + Each attempted size means calling allocatePartitions again with + one request's size having changed. + + After taking into account several factors that may limit the + maximum size of a requested partition, we arrive at a firm + maximum number of sectors by which a request can potentially grow. + + An initial attempt is made to allocate the full maximum size. If + this fails, we begin a rough binary search with a maximum of three + iterations to settle on a new size. + + TODO: Call disk.maximizePartition for each growable partition that + has not been allocated its full share of the free space upon + termination of each disk's loop iteration. Any detected + maximum size can be specified via a parted Constraint. + + Arguments: + + disks -- a list of all usable disks (DiskDevice instances) + partitions -- a list of all partitions (PartitionDevice + instances) + """ + log.debug("growPartitions: disks=%s, partitions=%s" % ([d.name for d in disks], [p.name for p in partitions])) + all_growable = [p for p in partitions if p.req_grow] + + # sort requests by base size in decreasing order + all_growable.sort(key=lambda p: p.req_size, reverse=True) + + log.debug("growable requests are %s" % [p.name for p in all_growable]) + + for disk in disks: + log.debug("growing requests on %s" % disk.name) + sectorSize = disk.partedDisk.device.physicalSectorSize + # get a list of free space regions on the disk + free = disk.partedDisk.getFreeSpaceRegions() + # sort the free regions in decreasing order of size + free.sort(key=lambda r: r.length, reverse=True) + disk_free = reduce(lambda x,y: x + y, [f.length for f in free]) + + # make a list of partitions currently allocated on this disk + # -- they're already sorted + growable = [] + disk_total = 0 + for part in all_growable: + #log.debug("checking if part %s (%s) is on this disk" % (part.name, + # part.disk.name)) + if part.disk == disk: + growable.append(part) + disk_total += (part.req_size * (1024 * 1024)) / sectorSize + + # now we loop through the partitions... + for part in growable: + # calculate max number of sectors this request can grow + sectors = (part.req_size * (1024 * 1024)) / sectorSize + share = float(sectors) / float(disk_total) + max_grow = share * disk_free + max_mb = (max_grow * sectorSize) / (1024 * 1024) + log.debug("%s: base_size=%dMB, max_size=%sMB" % (part.name, + part.req_base_size, + part.req_max_size)) + log.debug("%s: %dMB (%d sectors, or %d%% of %d)" % (part.name, + max_mb, + max_grow, + share * 100, + disk_free)) + + log.debug("checking constraints on max size...") + # don't grow beyond the request's maximum size + if part.req_max_size: + log.debug("max_size: %dMB" % part.req_max_size) + # FIXME: round down to nearest cylinder boundary + max_sect = (part.req_max_size * (1024 * 1024)) / sectorSize + if max_sect < sectors + max_grow: + max_grow = (max_sect - sectors) + + # don't grow beyond the resident filesystem's max size + if part.format and getattr(part.format, 'maxsize', 0): + log.debug("format maxsize: %dMB" % part.format.maxsize) + # FIXME: round down to nearest cylinder boundary + max_sect = (part.format.maxsize * (1024 * 1024)) / sectorSize + if max_sect < sectors + max_grow: + max_grow = (max_sect - sectors) + + # we can only grow as much as the largest free region on the disk + if free[0].length < max_grow: + log.debug("largest free region: %d sectors (%dMB)" % (free[0].length, free[0].getSize())) + # FIXME: round down to nearest cylinder boundary + max_grow = free[0].length + + # Now, we try to grow this partition as close to max_grow + # sectors as we can. + # + # We could call allocatePartitions after modifying this + # request and saving the original value of part.req_size, + # or we could try to use disk.maximizePartition(). + req_sectors = (part.req_size * 1024 * 1024) / sectorSize + max_sectors = req_sectors + max_grow + max_size = (max_sectors * sectorSize) / (1024 * 1024) + orig_size = part.req_size + # try the max size to begin with + log.debug("attempting to allocate maximum size: %dMB" % max_size) + part.req_size = max_size + try: + allocatePartitions(disks, partitions) + except PartitioningError, e: + log.debug("max size attempt failed: %s (%dMB)" % (part.name, + max_size)) + part.req_size = orig_size + else: + continue + + log.debug("starting binary search: size=%d max_size=%d" % (part.req_size, max_size)) + count = 0 + op_func = add + increment = max_grow + last_good_size = part.req_size + last_outcome = None + while part.req_size < max_size and count < 3: + last_size = part.req_size + increment /= 2 + sectors = op_func(sectors, increment) + part.req_size = (sectors * sectorSize) / (1024 * 1024) + log.debug("attempting size=%dMB" % part.req_size) + count += 1 + try: + allocatePartitions(disks, partitions) + except PartitioningError, e: + log.debug("attempt at %dMB failed" % part.req_size) + op_func = sub + last_outcome = False + else: + last_good_size = part.req_size + last_outcome = True + + if not last_outcome: + part.req_size = last_good_size + log.debug("backing up to size=%dMB" % part.req_size) + try: + allocatePartitions(disks, partitions) + except PartitioningError, e: + raise PartitioningError("failed to grow partitions") + + # TODO: call disk.maximizePartition with max_size as the + # constraint, in case it can grab some more free space + + + # reset all requests to their original requested size + for part in partitions: + if part.exists: + continue + part.req_size = part.req_base_size + +def growLVM(tree): + """ Grow LVs according to the sizes of the PVs. """ + vgs = tree.getDevicesByType("lvm vg") + for vg in vgs: + total_free = vg.freeSpace + if not total_free: + log.debug("vg %s has no free space" % vg.name) + continue + + # figure out how much to grow each LV + grow_amounts = {} + lv_total = vg.size - total_free + + # This first loop is to calculate percentage-based growth + # amounts. These are based on total free space. + for lv in lvs: + if not lv.req_grow or not lv.req_percent: + continue + + portion = (lv_req_percent * 0.01) + # clamp growth amount to a multiple of vg extent size + grow = vg.align(portion * vg.vgFree) + new_size = lv.req_size + grow + if lv.req_max_size and new_size > lv.req_max_size: + # clamp growth amount to a multiple of vg extent size + grow -= align(new_size - lv.req_max_size) + + # clamp growth amount to a multiple of vg extent size + grow_amounts[lv.name] = vg.align(grow) + total_free -= grow + + # This second loop is to calculate non-percentage-based growth + # amounts. These are based on free space remaining after + # calculating percentage-based growth amounts. + for lv in lvs: + if not lv.req_grow or lv.req_percent: + continue + + portion = float(lv.req_size) / float(lv_total) + # clamp growth amount to a multiple of vg extent size + grow = vg.align(portion * total_free) + new_size = lv.req_size + grow + if lv.req_max_size and new_size > lv.req_max_size: + # clamp growth amount to a multiple of vg extent size + grow -= vg.align(new_size - lv.req_max_size) + + grow_amounts[lv.name] = grow + + if not grow_amounts: + log.debug("no growable lvs in vg %s" % vg.name) + continue + + # now grow the lvs by the amounts we've calculated above + for lv in lvs: + if lv.name not in grow_amounts.keys(): + continue + lv.size = new_size + + # now there shouldn't be any free space left, but if there is we + # should allocate it to one of the LVs + vg_free = vg.freeSpace + log.debug("vg %s still has %dMB free" % (vg.name, vg_free)) + + + diff --git a/storage/storage_log.py b/storage/storage_log.py new file mode 100644 index 000000000..351df9811 --- /dev/null +++ b/storage/storage_log.py @@ -0,0 +1,13 @@ +import logging + + +#handler = logging.StreamHandler() +handler = logging.FileHandler("/tmp/storage.log") +formatter = logging.Formatter("[%(asctime)s] %(levelname)8s: %(message)s") +handler.setFormatter(formatter) + +logger = logging.getLogger("storage") +logger.addHandler(handler) +logger.setLevel(logging.DEBUG) + + diff --git a/storage/udev.py b/storage/udev.py new file mode 100644 index 000000000..cfc7837e6 --- /dev/null +++ b/storage/udev.py @@ -0,0 +1,273 @@ +# udev.py +# Python module for querying the udev database for device information. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# + +import os +import re + +import iutil + +import logging +log = logging.getLogger("storage") + + +def udev_get_block_devices(): + udev_settle(timeout=2) + entries = [] + for path in enumerate_block_devices(): + entry = udev_get_block_device(path) + if entry: + entries.append(entry) + return entries + +def enumerate_block_devices(): + top_dir = "/sys/class/block" + devices = [] + for dev_name in os.listdir(top_dir): + if dev_name.startswith("loop") or dev_name.startswith("ram"): + continue + full_path = os.path.join(top_dir, dev_name) + link_ref = os.readlink(full_path) + real_path = os.path.join(top_dir, link_ref) + sysfs_path = os.path.normpath(real_path) + devices.append(sysfs_path) + return devices + +def udev_get_block_device(sysfs_path): + log.debug("getting udev info for %s" % sysfs_path) + if not os.path.exists(sysfs_path): + log.debug("%s does not exist" % sysfs_path) + return None + + db_entry = sysfs_path[4:].replace("/", "\\x2f") + db_root = "/dev/.udev/db" + db_path = os.path.normpath("%s/%s" % (db_root, db_entry)) + if not os.access(db_path, os.R_OK): + log.debug("db entry %s does not exist" % db_path) + return None + + entry = open(db_path).read() + dev = udev_parse_block_entry(entry) + if dev: + # XXX why do we do this? is /sys going to move during installation? + dev['sysfs_path'] = sysfs_path[4:] # strip off the leading '/sys' + dev = udev_parse_uevent_file(dev) + + # now add in the contents of the uevent file since they're handy + return dev + +def udev_parse_uevent_file(dev): + path = os.path.normpath("/sys/%s/uevent" % dev['sysfs_path']) + if not os.access(path, os.R_OK): + return dev + + with open(path) as f: + for line in f.readlines(): + (key, equals, value) = line.strip().partition("=") + if not equals: + continue + + dev[key] = value + + return dev + +def udev_parse_block_entry(buf): + dev = {'name': None, + 'symlinks': []} + + for line in buf.splitlines(): + line.strip() + (tag, sep, val) = line.partition(":") + if not sep: + continue + + if tag == "N": + dev['name'] = val + elif tag == "S": + dev['symlinks'].append(val) + elif tag == "E": + if val.count("=") > 1 and val.count(" ") == 1: + # eg: LVM2_LV_NAME when querying the VG for its LVs + vars = val.split() + vals = [] + var_name = None + for (index, subval) in enumerate(vars): + (var_name, sep, var_val) = subval.partition("=") + if sep: + vals.append(var_val) + + dev[var_name] = vals + else: + (var_name, sep, var_val) = val.partition("=") + if not sep: + continue + + if var_val.count(" "): + # eg: DEVLINKS + var_val = var_val.split() + + dev[var_name] = var_val + + if dev.get("name"): + return dev + +def udev_settle(timeout=None): + argv = ["settle"] + if timeout: + argv.append("--timeout=%d" % int(timeout)) + + iutil.execWithRedirect("udevadm", argv, stderr="/dev/null", searchPath=1) + +def udev_trigger(subsystem=None): + argv = ["trigger"] + if subsystem: + argv.append("--subsystem-match=%s" % subsystem) + + iutil.execWithRedirect("udevadm", argv, stderr="/dev/null", searchPath=1) + + +""" These are functions for retrieving specific pieces of information from + udev database entries. +""" +def udev_device_get_name(udev_info): + """ Return the best name for a device based on the udev db data. """ + return udev_info.get("DM_NAME", udev_info["name"]) + +def udev_device_get_format(udev_info): + """ Return a device's format type as reported by udev. """ + return udev_info.get("ID_FS_TYPE") + +def udev_device_get_uuid(udev_info): + """ Get the UUID from the device's format as reported by udev. """ + md_uuid = udev_info.get("MD_UUID") + uuid = udev_info.get("ID_FS_UUID") + # we don't want to return the array's uuid as a member's uuid + if uuid and not md_uuid == uuid: + return udev_info.get("ID_FS_UUID") + +def udev_device_get_label(udev_info): + """ Get the label from the device's format as reported by udev. """ + return udev_info.get("ID_FS_LABEL") + +def udev_device_is_dm(info): + """ Return True if the device is a device-mapper device. """ + return info.has_key("DM_NAME") + +def udev_device_is_md(info): + """ Return True is the device is an mdraid array device. """ + return info.has_key("MD_METADATA") + +def udev_device_is_cdrom(info): + """ Return True if the device is an optical drive. """ + # FIXME: how can we differentiate USB drives from CD-ROM drives? + # -- USB drives also generate a sdX device. + return info.get("ID_CDROM") == "1" + +def udev_device_is_disk(info): + """ Return True is the device is a disk. """ + has_range = os.path.exists("/sys/%s/range" % info['sysfs_path']) + return info.get("DEVTYPE") == "disk" or has_range + +def udev_device_is_partition(info): + has_start = os.path.exists("/sys/%s/start" % info['sysfs_path']) + return info.get("DEVTYPE") == "partition" or has_start + +def udev_device_get_sysfs_path(info): + return info['sysfs_path'] + +def udev_device_get_major(info): + return int(info["MAJOR"]) + +def udev_device_get_minor(info): + return int(info["MINOR"]) + +def udev_device_get_md_level(info): + return info["MD_LEVEL"] + +def udev_device_get_md_devices(info): + return int(info["MD_DEVICES"]) + +def udev_device_get_md_uuid(info): + return info["MD_UUID"] + +def udev_device_get_vg_name(info): + return info['LVM2_VG_NAME'] + +def udev_device_get_vg_uuid(info): + return info['LVM2_VG_UUID'] + +def udev_device_get_vg_size(info): + # lvm's decmial precision is not configurable, so we tell it to use + # KB and convert to MB here + return float(info['LVM2_VG_SIZE']) / 1024 + +def udev_device_get_vg_free(info): + # lvm's decmial precision is not configurable, so we tell it to use + # KB and convert to MB here + return float(info['LVM2_VG_FREE']) / 1024 + +def udev_device_get_vg_extent_size(info): + # lvm's decmial precision is not configurable, so we tell it to use + # KB and convert to MB here + return float(info['LVM2_VG_EXTENT_SIZE']) / 1024 + +def udev_device_get_vg_extent_count(info): + return int(info['LVM2_VG_EXTENT_COUNT']) + +def udev_device_get_vg_free_extents(info): + return int(info['LVM2_VG_FREE_COUNT']) + +def udev_device_get_vg_pv_count(info): + return int(info['LVM2_PV_COUNT']) + +def udev_device_get_pv_pe_start(info): + # lvm's decmial precision is not configurable, so we tell it to use + # KB and convert to MB here + return float(info['LVM2_PE_START']) / 1024 + +def udev_device_get_lv_names(info): + names = info['LVM2_LV_NAME'] + if not names: + names = [] + elif not isinstance(names, list): + names = [names] + return names + +def udev_device_get_lv_uuids(info): + uuids = info['LVM2_LV_UUID'] + if not uuids: + uuids = [] + elif not isinstance(uuids, list): + uuids = [uuids] + return uuids + +def udev_device_get_lv_sizes(info): + # lvm's decmial precision is not configurable, so we tell it to use + # KB and convert to MB here + sizes = info['LVM2_LV_SIZE'] + if not sizes: + sizes = [] + elif not isinstance(sizes, list): + sizes = [sizes] + + return [float(s) / 1024 for s in sizes] + + diff --git a/storage/zfcp.py b/storage/zfcp.py new file mode 100644 index 000000000..e38b8186b --- /dev/null +++ b/storage/zfcp.py @@ -0,0 +1,262 @@ +# +# zfcp.py - mainframe zfcp configuration install data +# +# Copyright (C) 2001, 2002, 2003, 2004 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 . +# +# Author(s): Karsten Hopp +# + +import string +import os +import iutil +import isys +import shutil +from constants import * + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) + +import logging +log = logging.getLogger("anaconda") +import warnings + +def loggedWriteLineToFile(fn, value): + f = open(fn, "w") + log.debug("echo %s > %s" % (value, fn)) + f.write("%s\n" % (value)) + f.close() + +zfcpsysfs = "/sys/bus/ccw/drivers/zfcp" +scsidevsysfs = "/sys/bus/scsi/devices" + +class ZFCPDevice: + def __init__(self, devnum, wwpn, fcplun): + self.devnum = self.sanitizeDeviceInput(devnum) + self.wwpn = self.sanitizeWWPNInput(wwpn) + self.fcplun = self.sanitizeFCPLInput(fcplun) + + if not self.checkValidDevice(self.devnum): + raise ValueError, _("You have not specified a device number or the number is invalid") + if not self.checkValidWWPN(self.wwpn): + raise ValueError, _("You have not specified a worldwide port name or the name is invalid.") + if not self.checkValidFCPLun(self.fcplun): + raise ValueError, _("You have not specified a FCP LUN or the number is invalid.") + + def __str__(self): + return "%s %s %s" %(self.devnum, self.wwpn, self.fcplun) + + def sanitizeDeviceInput(self, dev): + if dev is None or dev == "": + return None + dev = dev.lower() + bus = dev[:string.rfind(dev, ".") + 1] + dev = dev[string.rfind(dev, ".") + 1:] + dev = "0" * (4 - len(dev)) + dev + if not len(bus): + return "0.0." + dev + else: + return bus + dev + + def sanitizeWWPNInput(self, id): + if id is None or id == "": + return None + id = id.lower() + if id[:2] != "0x": + return "0x" + id + return id + + # ZFCP LUNs are usually entered as 16 bit, sysfs accepts only 64 bit + # (#125632), expand with zeroes if necessary + def sanitizeFCPLInput(self, lun): + if lun is None or lun == "": + return None + lun = lun.lower() + if lun[:2] == "0x": + lun = lun[2:] + lun = "0x" + "0" * (4 - len(lun)) + lun + lun = lun + "0" * (16 - len(lun) + 2) + return lun + + def _hextest(self, hex): + try: + int(hex, 16) + return True + except: + return False + + def checkValidDevice(self, id): + if id is None or id == "": + return False + if len(id) != 8: # p.e. 0.0.0600 + return False + if id[0] not in string.digits or id[2] not in string.digits: + return False + if id[1] != "." or id[3] != ".": + return False + return self._hextest(id[4:]) + + def checkValid64BitHex(self, hex): + if hex is None or hex == "": + return False + if len(hex) != 18: + return False + return self._hextest(hex) + checkValidWWPN = checkValidFCPLun = checkValid64BitHex + + def onlineDevice(self): + online = "%s/%s/online" %(zfcpsysfs, self.devnum) + portadd = "%s/%s/port_add" %(zfcpsysfs, self.devnum) + unitadd = "%s/%s/%s/unit_add" %(zfcpsysfs, self.devnum, self.wwpn) + + try: + if not os.path.exists(unitadd): + loggedWriteLineToFile(portadd, self.wwpn) + + loggedWriteLineToFile(unitadd, self.fcplun) + loggedWriteLineToFile(online, "1") + except Exception, e: + log.warn("error bringing zfcp device %s online: %s" + %(self.devnum, e)) + return False + + return True + + def offlineSCSIDevice(self): + f = open("/proc/scsi/scsi", "r") + lines = f.readlines() + f.close() + + for line in lines: + if not line.startswith("Host"): + continue + scsihost = string.split(line) + host = scsihost[1] + channel = scsihost[3] + id = scsihost[5] + lun = scsihost[7] + scsidev = "%s:%s:%s:%s" % (host[4], channel[1], id[1], lun[1]) + fcpsysfs = "%s/%s/fcp_lun" % (scsidevsysfs, scsidev) + fcpdel = "%s/%s/delete" % (scsidevsysfs, scsidev) + + f = open(fcpsysfs, "r") + fcplunsysfs = f.readline() + f.close() + + if fcplunsysfs[:6] == self.fcplun[:6]: + loggedWriteLineToFile(fcpdel, "1") + break + + def offlineDevice(self): + offline = "%s/%s/online" %(zfcpsysfs, self.devnum) + portremove = "%s/%s/port_remove" %(zfcpsysfs, self.devnum) + unitremove = "%s/%s/%s/unit_remove" %(zfcpsysfs, self.devnum, self.wwpn) + + try: + self.offlineSCSIDevice() + loggedWriteLineToFile(offline, "0") + loggedWriteLineToFile(unitremove, self.fcplun) + loggedWriteLineToFile(portremove, self.wwpn) + except Exception, e: + log.warn("error bringing zfcp device %s offline: %s" + %(self.devnum, e)) + return False + + return True + +class ZFCP: + def __init__(self): + self.fcpdevs = [] + self.hasReadConfig = False + + def readConfig(self): + try: + f = open("/tmp/fcpconfig", "r") + except: + log.info("no /tmp/fcpconfig; not configuring zfcp") + return + + lines = f.readlines() + f.close() + for line in lines: + # each line is a string separated list of values to describe a dev + # there are two valid formats for the line: + # devnum scsiid wwpn scsilun fcplun (scsiid + scsilun ignored) + # devnum wwpn fcplun + line = string.strip(line).lower() + if line.startswith("#"): + continue + fcpconf = string.split(line) + if len(fcpconf) == 3: + devnum = fcpconf[0] + wwpn = fcpconf[1] + fcplun = fcpconf[2] + elif len(fcpconf) == 5: + warnings.warn("SCSI ID and SCSI LUN values for ZFCP devices are ignored and deprecated.", DeprecationWarning) + devnum = fcpconf[0] + wwpn = fcpconf[2] + fcplun = fcpconf[4] + else: + log.warn("Invalid line found in /tmp/fcpconfig!") + continue + + try: + self.addFCP(devnum, wwpn, fcplun) + except ValueError, e: + log.warn("Invalid FCP device configuration: %s" %(e,)) + continue + + def addFCP(self, devnum, wwpn, fcplun): + d = ZFCPDevice(devnum, wwpn, fcplun) + if d.onlineDevice(): + self.fcpdevs.append(d) + + def shutdown(self): + if len(self.fcpdevs) == 0: + return + for d in self.fcpdevs: + d.offlineDevice() + + def startup(self): + if not self.hasReadConfig: + self.readConfig() + self.hasReadConfig = True + + if len(self.fcpdevs) == 0: + return + for d in self.fcpdevs: + d.onlineDevice() + + def writeKS(self, f): + if len(self.fcpdevs) == 0: + return + for d in self.fcpdevs: + f.write("zfcp --devnum %s --wwpn %s --fcplun %s\n" %(d.devnum, + d.wwpn, + d.fcplun)) + + def write(self, instPath): + if len(self.fcpdevs) == 0: + return + f = open(instPath + "/etc/zfcp.conf", "w") + for d in self.fcpdevs: + f.write("%s\n" %(d,)) + f.close() + + f = open(instPath + "/etc/modprobe.conf", "a") + f.write("alias scsi_hostadapter zfcp\n") + f.close() + +# vim:tw=78:ts=4:et:sw=4 diff --git a/zfcp.py b/zfcp.py deleted file mode 100644 index e38b8186b..000000000 --- a/zfcp.py +++ /dev/null @@ -1,262 +0,0 @@ -# -# zfcp.py - mainframe zfcp configuration install data -# -# Copyright (C) 2001, 2002, 2003, 2004 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 . -# -# Author(s): Karsten Hopp -# - -import string -import os -import iutil -import isys -import shutil -from constants import * - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - -import logging -log = logging.getLogger("anaconda") -import warnings - -def loggedWriteLineToFile(fn, value): - f = open(fn, "w") - log.debug("echo %s > %s" % (value, fn)) - f.write("%s\n" % (value)) - f.close() - -zfcpsysfs = "/sys/bus/ccw/drivers/zfcp" -scsidevsysfs = "/sys/bus/scsi/devices" - -class ZFCPDevice: - def __init__(self, devnum, wwpn, fcplun): - self.devnum = self.sanitizeDeviceInput(devnum) - self.wwpn = self.sanitizeWWPNInput(wwpn) - self.fcplun = self.sanitizeFCPLInput(fcplun) - - if not self.checkValidDevice(self.devnum): - raise ValueError, _("You have not specified a device number or the number is invalid") - if not self.checkValidWWPN(self.wwpn): - raise ValueError, _("You have not specified a worldwide port name or the name is invalid.") - if not self.checkValidFCPLun(self.fcplun): - raise ValueError, _("You have not specified a FCP LUN or the number is invalid.") - - def __str__(self): - return "%s %s %s" %(self.devnum, self.wwpn, self.fcplun) - - def sanitizeDeviceInput(self, dev): - if dev is None or dev == "": - return None - dev = dev.lower() - bus = dev[:string.rfind(dev, ".") + 1] - dev = dev[string.rfind(dev, ".") + 1:] - dev = "0" * (4 - len(dev)) + dev - if not len(bus): - return "0.0." + dev - else: - return bus + dev - - def sanitizeWWPNInput(self, id): - if id is None or id == "": - return None - id = id.lower() - if id[:2] != "0x": - return "0x" + id - return id - - # ZFCP LUNs are usually entered as 16 bit, sysfs accepts only 64 bit - # (#125632), expand with zeroes if necessary - def sanitizeFCPLInput(self, lun): - if lun is None or lun == "": - return None - lun = lun.lower() - if lun[:2] == "0x": - lun = lun[2:] - lun = "0x" + "0" * (4 - len(lun)) + lun - lun = lun + "0" * (16 - len(lun) + 2) - return lun - - def _hextest(self, hex): - try: - int(hex, 16) - return True - except: - return False - - def checkValidDevice(self, id): - if id is None or id == "": - return False - if len(id) != 8: # p.e. 0.0.0600 - return False - if id[0] not in string.digits or id[2] not in string.digits: - return False - if id[1] != "." or id[3] != ".": - return False - return self._hextest(id[4:]) - - def checkValid64BitHex(self, hex): - if hex is None or hex == "": - return False - if len(hex) != 18: - return False - return self._hextest(hex) - checkValidWWPN = checkValidFCPLun = checkValid64BitHex - - def onlineDevice(self): - online = "%s/%s/online" %(zfcpsysfs, self.devnum) - portadd = "%s/%s/port_add" %(zfcpsysfs, self.devnum) - unitadd = "%s/%s/%s/unit_add" %(zfcpsysfs, self.devnum, self.wwpn) - - try: - if not os.path.exists(unitadd): - loggedWriteLineToFile(portadd, self.wwpn) - - loggedWriteLineToFile(unitadd, self.fcplun) - loggedWriteLineToFile(online, "1") - except Exception, e: - log.warn("error bringing zfcp device %s online: %s" - %(self.devnum, e)) - return False - - return True - - def offlineSCSIDevice(self): - f = open("/proc/scsi/scsi", "r") - lines = f.readlines() - f.close() - - for line in lines: - if not line.startswith("Host"): - continue - scsihost = string.split(line) - host = scsihost[1] - channel = scsihost[3] - id = scsihost[5] - lun = scsihost[7] - scsidev = "%s:%s:%s:%s" % (host[4], channel[1], id[1], lun[1]) - fcpsysfs = "%s/%s/fcp_lun" % (scsidevsysfs, scsidev) - fcpdel = "%s/%s/delete" % (scsidevsysfs, scsidev) - - f = open(fcpsysfs, "r") - fcplunsysfs = f.readline() - f.close() - - if fcplunsysfs[:6] == self.fcplun[:6]: - loggedWriteLineToFile(fcpdel, "1") - break - - def offlineDevice(self): - offline = "%s/%s/online" %(zfcpsysfs, self.devnum) - portremove = "%s/%s/port_remove" %(zfcpsysfs, self.devnum) - unitremove = "%s/%s/%s/unit_remove" %(zfcpsysfs, self.devnum, self.wwpn) - - try: - self.offlineSCSIDevice() - loggedWriteLineToFile(offline, "0") - loggedWriteLineToFile(unitremove, self.fcplun) - loggedWriteLineToFile(portremove, self.wwpn) - except Exception, e: - log.warn("error bringing zfcp device %s offline: %s" - %(self.devnum, e)) - return False - - return True - -class ZFCP: - def __init__(self): - self.fcpdevs = [] - self.hasReadConfig = False - - def readConfig(self): - try: - f = open("/tmp/fcpconfig", "r") - except: - log.info("no /tmp/fcpconfig; not configuring zfcp") - return - - lines = f.readlines() - f.close() - for line in lines: - # each line is a string separated list of values to describe a dev - # there are two valid formats for the line: - # devnum scsiid wwpn scsilun fcplun (scsiid + scsilun ignored) - # devnum wwpn fcplun - line = string.strip(line).lower() - if line.startswith("#"): - continue - fcpconf = string.split(line) - if len(fcpconf) == 3: - devnum = fcpconf[0] - wwpn = fcpconf[1] - fcplun = fcpconf[2] - elif len(fcpconf) == 5: - warnings.warn("SCSI ID and SCSI LUN values for ZFCP devices are ignored and deprecated.", DeprecationWarning) - devnum = fcpconf[0] - wwpn = fcpconf[2] - fcplun = fcpconf[4] - else: - log.warn("Invalid line found in /tmp/fcpconfig!") - continue - - try: - self.addFCP(devnum, wwpn, fcplun) - except ValueError, e: - log.warn("Invalid FCP device configuration: %s" %(e,)) - continue - - def addFCP(self, devnum, wwpn, fcplun): - d = ZFCPDevice(devnum, wwpn, fcplun) - if d.onlineDevice(): - self.fcpdevs.append(d) - - def shutdown(self): - if len(self.fcpdevs) == 0: - return - for d in self.fcpdevs: - d.offlineDevice() - - def startup(self): - if not self.hasReadConfig: - self.readConfig() - self.hasReadConfig = True - - if len(self.fcpdevs) == 0: - return - for d in self.fcpdevs: - d.onlineDevice() - - def writeKS(self, f): - if len(self.fcpdevs) == 0: - return - for d in self.fcpdevs: - f.write("zfcp --devnum %s --wwpn %s --fcplun %s\n" %(d.devnum, - d.wwpn, - d.fcplun)) - - def write(self, instPath): - if len(self.fcpdevs) == 0: - return - f = open(instPath + "/etc/zfcp.conf", "w") - for d in self.fcpdevs: - f.write("%s\n" %(d,)) - f.close() - - f = open(instPath + "/etc/modprobe.conf", "a") - f.write("alias scsi_hostadapter zfcp\n") - f.close() - -# vim:tw=78:ts=4:et:sw=4 -- cgit From bb5ad8aa8c5e2f4d6234a5b545fd8bea53b560ee Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 12:18:18 -0600 Subject: Return device instances, not paths, from findExistingRoots. --- storage/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index e44a8b825..cd890fb2d 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -543,7 +543,7 @@ def findExistingRootDevices(anaconda, upgradeany=False): if os.access(anaconda.rootPath + "/etc/fstab", os.R_OK): relstr = getReleaseString(anaconda.rootPath) if upgradeany or productMatches(relstr, productName): - rootDevs.append((device.path, relstr)) + rootDevs.append((device, relstr)) # this handles unmounting the filesystem device.teardown(recursive=True) @@ -561,7 +561,7 @@ def mountExistingSystem(anaconda, rootDevice, else: readOnly = "" - if rootDevice.path in anaconda.id.storage.protectedPartitions and \ + if rootDevice.name in anaconda.id.storage.protectedPartitions and \ os.path.ismount("/mnt/isodir"): isys.mount("/mnt/isodir", rootPath, -- cgit From 715e529c7ae2742ff57f7b9eba055e92e8077d82 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 12:20:52 -0600 Subject: Add udev rules until device-mapper starts using udev. --- 70-anaconda.rules | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 70-anaconda.rules diff --git a/70-anaconda.rules b/70-anaconda.rules new file mode 100644 index 000000000..b203ed26b --- /dev/null +++ b/70-anaconda.rules @@ -0,0 +1,29 @@ +ACTION!="add|change", GOTO="anaconda_end" +SUBSYSTEM!="block", GOTO="anaconda_end" + +KERNEL!="dm-*", GOTO="anaconda_raid_probe" + +IMPORT{program}="/sbin/dmsetup info -c --nameprefixes --unquoted --rows --noheadings -o name,uuid,suspended,readonly,major,minor,open,tables_loaded -j%M -m%m" +ENV{DM_NAME}!="?*", GOTO="anaconda_end" + +SYMLINK+="disk/by-id/dm-name-$env{DM_NAME}" +ENV{DM_UUID}=="?*", SYMLINK+="disk/by-id/dm-uuid-$env{DM_UUID}" + +ENV{DM_STATE}=="SUSPENDED", GOTO="anaconda_end" + +IMPORT{program}="vol_id --export $tempnode" +OPTIONS="link_priority=-100" +ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}" + +LABEL="anaconda_raid_probe" + +# probe raid metadata of mdraid member devices +ENV{ID_FS_TYPE}=="linux_raid_member", IMPORT{program}="/sbin/mdadm --examine --export $root/%k" + +# probe metadata of LVM2 physical volumes +ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/sbin/lvm pvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -opv_name,pv_uuid,pv_size,vg_name,vg_uuid,pv_pe_count,pv_pe_alloc_count,pe_start $root/%k" +ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/sbin/lvm vgs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -ouuid,size,free,extent_size,extent_count,free_count,pv_count $env{LVM2_VG_NAME}" +ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/sbin/lvm lvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -olv_name,lv_uuid,lv_size $env{LVM2_VG_NAME}" + +LABEL="anaconda_end" + -- cgit From e1a7fe9887886044b07587b3ec2caa2ff53ebfb2 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 12:21:24 -0600 Subject: Updates to make existing code use the new storage module. --- anaconda | 3 +- backend.py | 7 +- bootloader.py | 51 +++----- dispatch.py | 10 +- exception.py | 2 +- installclass.py | 17 ++- installmethod.py | 6 +- instdata.py | 36 ++---- isys/isys.py | 358 +------------------------------------------------------ kickstart.py | 56 +++++---- livecd.py | 39 +++--- network.py | 4 +- packages.py | 137 ++++++++++++--------- rescue.py | 46 ++++--- upgrade.py | 207 +++++--------------------------- yuminstall.py | 26 ++-- 16 files changed, 236 insertions(+), 769 deletions(-) diff --git a/anaconda b/anaconda index f669ee0e8..199168b05 100755 --- a/anaconda +++ b/anaconda @@ -983,11 +983,10 @@ if __name__ == "__main__": # Skip the disk options in rootpath mode if flags.rootpath: - anaconda.dispatch.skipStep("partitionobjinit", permanent = 1) anaconda.dispatch.skipStep("parttype", permanent = 1) anaconda.dispatch.skipStep("autopartitionexecute", permanent = 1) anaconda.dispatch.skipStep("partition", permanent = 1) - anaconda.dispatch.skipStep("partitiondone", permanent = 1) + anaconda.dispatch.skipStep("storagedone", permanent = 1) anaconda.dispatch.skipStep("bootloader", permanent = 1) anaconda.dispatch.skipStep("bootloaderadvanced", permanent = 1) anaconda.dispatch.skipStep("upgbootloader", permanent = 1) diff --git a/backend.py b/backend.py index e8bcf4f5c..475785b12 100644 --- a/backend.py +++ b/backend.py @@ -85,8 +85,8 @@ class AnacondaBackend: # the initrd might need iscsi-initiator-utils, and chances are # it was not installed yet the first time mkinitrd was run, as # mkinitrd does not require it. - for disk in anaconda.id.diskset.disks.keys(): - if isys.driveIsIscsi(disk): + for disk in anaconda.id.storage.disks: + if isys.driveIsIscsi(disk.path): has_iscsi_disk = True break @@ -156,8 +156,9 @@ class AnacondaBackend: if not anaconda.mediaDevice or not os.path.exists(installimg): return + free = anaconda.id.storage.fsset.fsFreeSpace(anaconda.rootPath) self._loopbackFile = "%s%s/rhinstall-install.img" % (anaconda.rootPath, - anaconda.id.fsset.filesystemSpace(anaconda.rootPath)[0][0]) + free[0][0]) try: win = anaconda.intf.waitWindow(_("Copying File"), diff --git a/bootloader.py b/bootloader.py index b78cfd701..7ad11b4d5 100644 --- a/bootloader.py +++ b/bootloader.py @@ -60,49 +60,36 @@ def bootloaderSetupChoices(anaconda): anaconda.id.bootloader.updateDriveList(pref) if iutil.isEfi() and not anaconda.id.bootloader.device: - drives = anaconda.id.diskset.disks.keys() - drives.sort() bootPart = None - for drive in drives: - disk = anaconda.id.diskset.disks[drive] - for part in disk.partitions: - if part.active and partedUtils.isEfiSystemPartition(part): - bootPart = part.getDeviceNodeName() + partitions = anaconda.id.storage.partitions + for part in partitions: + if part.partedPartition.active and \ + partedUtils.isEfiSystemPartition(part.partedPartition): + bootPart = part.name break - if bootPart: - break if bootPart: anaconda.id.bootloader.setDevice(bootPart) - dev = Device() - dev.device = bootPart - anaconda.id.fsset.add(FileSystemSetEntry(dev, None, fileSystemTypeGet("efi"))) # iSeries bootloader on upgrades if iutil.getPPCMachine() == "iSeries" and not anaconda.id.bootloader.device: - drives = anaconda.id.diskset.disks.keys() - drives.sort() bootPart = None - for drive in drives: - disk = anaconda.id.diskset.disks[drive] - for part in disk.partitions: - if part.active and part.getFlag(parted.PARTITION_PREP): - bootPart = part.getDeviceNodeName() - break - if bootPart: + partitions = anaconda.id.storage.partitions + for part in partitions: + if part.partedPartition.active and \ + part.partedPartition.getFlag(parted.PARTITION_PREP): + bootPart = part.name break if bootPart: anaconda.id.bootloader.setDevice(bootPart) - dev = Device() - dev.device = bootPart - anaconda.id.fsset.add(FileSystemSetEntry(dev, None, fileSystemTypeGet("PPC PReP Boot"))) - choices = anaconda.id.fsset.bootloaderChoices(anaconda.id.diskset, anaconda.id.bootloader) + choices = anaconda.id.storage.fsset.bootloaderChoices(anaconda.id.bootloader) if not choices and iutil.getPPCMachine() != "iSeries": anaconda.dispatch.skipStep("instbootloader") else: anaconda.dispatch.skipStep("instbootloader", skip = 0) - anaconda.id.bootloader.images.setup(anaconda.id.diskset, anaconda.id.fsset) + # FIXME: ... + anaconda.id.bootloader.images.setup(anaconda.id.storage) if anaconda.id.bootloader.defaultDevice != None and choices: keys = choices.keys() @@ -150,11 +137,7 @@ def writeBootloader(anaconda): kernelList = [] otherList = [] - root = anaconda.id.fsset.getEntryByMountPoint('/') - if root: - rootDev = root.device.getDevice() - else: - rootDev = None + rootDev = getattr(anaconda.id.rootDevice, "path", None) defaultDev = anaconda.id.bootloader.images.getDefault() kernelLabel = None @@ -204,8 +187,10 @@ def writeBootloader(anaconda): dosync() try: - anaconda.id.bootloader.write(anaconda.rootPath, anaconda.id.fsset, anaconda.id.bootloader, - anaconda.id.instLanguage, kernelList, otherList, defaultDev, + anaconda.id.bootloader.write(anaconda.rootPath, anaconda.id.storage, + anaconda.id.bootloader, + anaconda.id.instLanguage, + kernelList, otherList, defaultDev, justConfigFile, anaconda.intf) if not justConfigFile: w.pop() diff --git a/dispatch.py b/dispatch.py index b773404dd..a09a86183 100644 --- a/dispatch.py +++ b/dispatch.py @@ -34,8 +34,9 @@ from packages import setupTimezone from packages import setFileCons from packages import regKeyScreen from packages import writeRegKey -from partitions import partitionObjectsInitialize -from partitions import partitioningComplete +from storage import storageInitialize +from storage import storageComplete +from storage.partitioning import doAutoPartition from bootloader import writeBootloader, bootloaderSetupChoices from flags import flags from upgrade import upgradeMountFilesystems, queryUpgradeArch @@ -71,13 +72,13 @@ installSteps = [ ("keyboard", ), ("betanag", betaNagScreen, ), ("regkey", regKeyScreen, ), + ("storageinit", storageInitialize, ), ("findrootparts", findRootParts, ), ("findinstall", ), ("network", ), ("timezone", ), ("accounts", ), ("setuptime", setupTimezone, ), - ("partitionobjinit", partitionObjectsInitialize, ), ("parttype", ), ("autopartitionexecute", doAutoPartition, ), ("partition", ), @@ -88,8 +89,7 @@ installSteps = [ ("addswap", ), ("upgrademigfind", upgradeMigrateFind, ), ("upgrademigratefs", ), - ("partitiondone", partitioningComplete, ), - ("migratefilesystems", doMigrateFilesystems, ), + ("storagedone", storageComplete, ), ("enablefilesystems", turnOnFilesystems, ), ("upgbootloader", ), ("bootloadersetup", bootloaderSetupChoices, ), diff --git a/exception.py b/exception.py index 7b6fdea9d..681b3ec5d 100644 --- a/exception.py +++ b/exception.py @@ -223,7 +223,7 @@ class AnacondaExceptionDump: for file in ("/tmp/syslog", "/tmp/anaconda.log", "/tmp/lvmout", "/tmp/resize.out", - "/tmp/program.log", + "/tmp/program.log", "/tmp/storage.log", anaconda.rootPath + "/root/install.log", anaconda.rootPath + "/root/upgrade.log"): try: diff --git a/installclass.py b/installclass.py index a3a97aed5..4a1e95269 100644 --- a/installclass.py +++ b/installclass.py @@ -96,14 +96,14 @@ class BaseInstallClass(object): "language", "keyboard", "welcome", + "storageinit", "findrootparts", "betanag", "installtype", - "partitionobjinit", "parttype", "autopartitionexecute", "partition", - "partitiondone", + "storagedone", "bootloadersetup", "bootloader", "network", @@ -116,7 +116,6 @@ class BaseInstallClass(object): "confirminstall", "install", "enablefilesystems", - "migratefilesystems", "setuptime", "preinstallconfig", "installpackages", @@ -188,11 +187,11 @@ class BaseInstallClass(object): from backend import AnacondaBackend return AnacondaBackend - def setDefaultPartitioning(self, partitions, clear = CLEARPART_TYPE_LINUX, + def setDefaultPartitioning(self, storage, clear = CLEARPART_TYPE_LINUX, doClear = 1, useLVM = True): autorequests = [ ("/", None, 1024, None, 1, 1, 1) ] - bootreq = getAutopartitionBoot(partitions) + bootreq = getAutopartitionBoot(storage) if bootreq: autorequests.extend(bootreq) @@ -200,13 +199,13 @@ class BaseInstallClass(object): autorequests.append((None, "swap", minswap, maxswap, 1, 1, 1)) if doClear: - partitions.autoClearPartType = clear - partitions.autoClearPartDrives = [] + storage.autoClearPartType = clear + storage.autoClearPartDrives = [] if useLVM: - partitions.autoPartitionRequests = autoCreateLVMPartitionRequests(autorequests) + storage.autoPartitionRequests = autoCreateLVMPartitionRequests(autorequests) else: - partitions.autoPartitionRequests = autoCreatePartitionRequests(autorequests) + storage.autoPartitionRequests = autoCreatePartitionRequests(autorequests) def setInstallData(self, anaconda): diff --git a/installmethod.py b/installmethod.py index ce1dd7f99..d62201583 100644 --- a/installmethod.py +++ b/installmethod.py @@ -47,9 +47,9 @@ def doMethodComplete(anaconda): isys.ejectCdrom(dev) mtab = "/dev/root / ext3 ro 0 0\n" - for ent in anaconda.id.fsset.entries: - if ent.mountpoint == "/": - mtab = "/dev/root / %s ro 0 0\n" %(ent.fsystem.name,) + rootDevice = anaconda.id.storage.rootDevice + if rootDevice: + mtab = "/dev/root / %s ro 0 0\n" % rootDevice.format.type f = open(anaconda.rootPath + "/etc/mtab", "w+") f.write(mtab) diff --git a/instdata.py b/instdata.py index 25aee6157..86f389363 100644 --- a/instdata.py +++ b/instdata.py @@ -30,12 +30,8 @@ import firewall import security import timezone import desktop -import fsset import bootloader -import partitions -import partedUtils -import iscsi -import zfcp +import storage import urllib import iutil import isys @@ -67,8 +63,6 @@ class InstallData: self.instClass = None self.network = network.Network() - self.iscsi = iscsi.iscsi() - self.zfcp = zfcp.ZFCP() self.firewall = firewall.Firewall() self.security = security.Security() self.timezone = timezone.Timezone() @@ -80,10 +74,7 @@ class InstallData: self.upgrade = None if flags.cmdline.has_key("preupgrade"): self.upgrade = True - # XXX move fsset and/or diskset into Partitions object? - self.fsset.reset() - self.diskset = partedUtils.DiskSet(self.anaconda) - self.partitions = partitions.Partitions(self.anaconda) + self.storage = storage.Storage(self.anaconda) self.bootloader = bootloader.getBootloader() self.upgradeRoot = None self.rootParts = None @@ -102,19 +93,13 @@ class InstallData: if os.path.exists("/dev/live") and \ stat.S_ISBLK(os.stat("/dev/live")[stat.ST_MODE]): target = os.readlink("/dev/live") - self.partitions.protected = [target] + self.storage.protectedPartitions = [target] elif self.anaconda.methodstr and self.anaconda.methodstr.startswith("hd:"): method = self.anaconda.methodstr[3:] - device = method.split(":", 3)[0] + devspec = method.split(":", 3)[0] - if device.startswith("LABEL="): - dev = isys.getDeviceByToken("LABEL", device[6:]) - elif device.startswith("UUID="): - dev = isys.getDeviceByToken("UUID", device[5:]) - else: - dev = device - - if dev is None: + device = storage.resolveDevice(devspec) + if device is None: if self.getUpgrade(): return else: @@ -125,10 +110,7 @@ class InstallData: type="custom", custom_buttons = [_("_Exit installer")]) sys.exit(1) - if dev.startswith("/dev/"): - dev = dev[5:] - - self.partitions.protected = [dev] + self.storage.protectedPartitions = [device.name] def setInstallProgressClass(self, c): self.instProgress = c @@ -286,7 +268,6 @@ class InstallData: if not self.isHeadless: self.keyboard.writeKS(f) self.network.writeKS(f) - self.zfcp.writeKS(f) if self.rootPassword["isCrypted"]: args = " --iscrypted %s" % self.rootPassword["password"] @@ -312,7 +293,7 @@ class InstallData: self.security.writeKS(f) self.timezone.writeKS(f) self.bootloader.writeKS(f) - self.partitions.writeKS(f) + self.storage.writeKS(f) # FIXME: unimplemented if self.backend is not None: self.backend.writeKS(f) @@ -339,6 +320,5 @@ class InstallData: self.videocard = None self.isHeadless = 0 self.extraModules = extraModules - self.fsset = fsset.FileSystemSet() self.reset() diff --git a/isys/isys.py b/isys/isys.py index e6c4b3476..fb2fa5d57 100755 --- a/isys/isys.py +++ b/isys/isys.py @@ -59,7 +59,6 @@ NM_STATE_CONNECTED = 3 NM_STATE_DISCONNECTED = 4 mountCount = {} -raidCount = {} MIN_RAM = _isys.MIN_RAM MIN_GUI_RAM = _isys.MIN_GUI_RAM @@ -71,194 +70,12 @@ 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. # @param readOnly Should this loopback device be used read-only? def losetup(device, file, readOnly = 0): + # FIXME: implement this as a storage.devices.Device subclass if readOnly: mode = os.O_RDONLY else: @@ -272,6 +89,7 @@ def losetup(device, file, readOnly = 0): os.close(targ) def lochangefd(device, file): + # FIXME: implement this as a storage.devices.Device subclass loop = os.open(device, os.O_RDONLY) targ = os.open(file, os.O_RDONLY) try: @@ -283,6 +101,7 @@ def lochangefd(device, file): ## Disable a previously setup loopback device. # @param device The full path to an existing loopback device node. def unlosetup(device): + # FIXME: implement this as a storage.devices.Device subclass loop = os.open(device, os.O_RDONLY) try: _isys.unlosetup(loop) @@ -407,175 +226,6 @@ def swapon (path): def loadKeymap(keymap): return _isys.loadKeymap (keymap) -cachedDrives = None - -## Clear the drive dict cache. -# This method clears the drive dict cache. If the drive state changes (by -# loading and unloading modules, attaching removable devices, etc.) then this -# function must be called before any of the *DriveDict or *DriveList functions. -# If not, those functions will return information that does not reflect the -# current machine state. -def flushDriveDict(): - global cachedDrives - cachedDrives = None - -def driveDict(klassArg): - import parted - global cachedDrives - if cachedDrives is None: - new = {} - for dev in minihal.get_devices_by_type("storage"): - if dev['device'] is None: # none devices make no sense - continue - - device = dev['device'].replace('/dev/','') - # we can't actually use the sg devices, so ignore them - if device.startswith("sg"): - log.info("ignoring sg device %s" %(device,)) - continue - - # we can't actually use the st devices, so ignore them - if device.startswith("st"): - log.info("ignoring st device %s" %(device,)) - continue - - # we want to ignore md devices as they're not hard disks in our pov - if device.startswith("md"): - continue - - if dev['storage.drive_type'] != 'disk': - new[device] = dev - continue - try: - if not mediaPresent (device): - new[device] = dev - continue - - # blacklist the device which the live image is running from - # installing over that is almost certainly the wrong - # thing to do. - if os.path.exists("/dev/live") and \ - stat.S_ISBLK(os.stat("/dev/live")[stat.ST_MODE]): - livetarget = os.path.realpath("/dev/live") - if livetarget.startswith(dev['device']): - log.info("%s looks to be the live device; ignoring" % (device,)) - continue - - if device.startswith("sd"): - peddev = parted.getDevice(dev['device']) - model = peddev.model - - # blacklist *STMF on power5 iSeries boxes - if iutil.isPPC() and \ - model.find("IBM *STMF KERNEL") != -1: - log.info("%s looks like STMF, ignoring" % (device,)) - del peddev - continue - - # blacklist PS3 flash - if iutil.isPPC() and \ - model.find("SCEI Flash-5") != -1: - log.info("%s looks like PS3 flash, ignoring" % \ - (device,)) - del peddev - continue - - # blacklist DGC/EMC LUNs for which we have no ACL. - # We should be ignoring LUN_Z for all vendors, but I - # don't know how (if) other vendors encode this into - # the model info. - # - # XXX I need to work some SCC2 LUN mode page detection - # into libbdevid, and then this should use that instead. - # -- pjones - if str(peddev.model) == "DGC LUNZ": - log.info("%s looks like a LUN_Z device, ignoring" % \ - (device,)) - del peddev - continue - - del peddev - new[device] = dev - except Exception, e: - log.debug("exception checking disk blacklist on %s: %s" % \ - (device, e)) - cachedDrives = new - - ret = {} - for key,dev in cachedDrives.items(): - # XXX these devices should have deviceclass attributes. Or they - # should all be subclasses in a device tree and we should be able - # to use isinstance on all of them. Not both. - if isinstance(dev, block.MultiPath) or isinstance(dev, block.RaidSet): - if klassArg == "disk": - ret[key] = dev - elif dev['storage.drive_type'] == klassArg: - ret[key] = dev - return ret - -## Get all the hard drives attached to the system. -# This method queries the drive dict cache for all hard drives. If the cache -# is empty, this will cause all disk devices to be probed. If the status of -# the devices has changed, flushDriveDict must be called first. -# -# @see flushDriveDict -# @see driveDict -# @return A dict of all the hard drive descriptions, keyed on device name. -def hardDriveDict(): - ret = {} - dict = driveDict("disk") - for item in dict.keys(): - try: - ret[item] = dict[item]['description'] - except AttributeError: - ret[item] = "" - return ret - -## Get all the removable drives attached to the system. -# This method queries the drive dict cache for all removable drives. If the cache -# is empty, this will cause all disk devices to be probed. If the status of -# the devices has changed, flushDriveDict must be run called first. -# -# @see flushDriveDict -# @see driveDict -# @return A dict of all the removable drive descriptions, keyed on device name. -def removableDriveDict(): - ret = {} - dict = driveDict("disk") - for item in dict.keys(): - if dict[item]['storage.removable'] != 0: - try: - ret[item] = dict[item]['description'] - except AttributeError: - ret[item] = "" - return ret - -## Get all CD/DVD drives attached to the system. -# This method queries the drive dict cache for all hard drives. If the cache -# is empty, this will cause all disk devices to be probed. If the status of -# the devices has changed, flushDriveDict must be called first. -# -# @see flushDriveDict -# @see driveDict -# @return A sorted list of all the CD/DVD drives, without any leading /dev/. -def cdromList(): - list = driveDict("cdrom").keys() - list.sort() - return list - -## Get all tape drives attached to the system. -# This method queries the drive dict cache for all hard drives. If the cache -# is empty, this will cause all disk devices to be probed. If the status of -# the devices has changed, flushDriveDict must be called first. -# -# @see flushDriveDict -# @see driveDict -# @return A sorted list of all the tape drives, without any leading /dev/. -def tapeDriveList(): - list = driveDict("tape").keys() - list.sort() - return list - def getDasdPorts(): return _isys.getDasdPorts() @@ -788,6 +438,7 @@ def ext2HasJournal(device): return hasjournal def ejectCdrom(device): + # XXX this should go into storage.devices.OpticalDevice if not os.path.exists(device): device = "/dev/%s" % device @@ -834,6 +485,7 @@ def driveUsesModule(device, modules): # @param device The basename of the device node. # @return True if media is present in device, False otherwise. def mediaPresent(device): + # XXX this should go into storage.devices.RemovableDevice or similar try: fd = os.open("/dev/%s" % device, os.O_RDONLY) except OSError, (errno, strerror): diff --git a/kickstart.py b/kickstart.py index 9a7099f25..0fee19e07 100644 --- a/kickstart.py +++ b/kickstart.py @@ -24,13 +24,11 @@ import isys import os import tempfile from autopart import * -from fsset import * from flags import flags from constants import * import sys import raid import string -import partRequests import urlgrabber.grabber as grabber import lvm import warnings @@ -149,8 +147,8 @@ class AutoPart(commands.autopart.F9_AutoPart): self.handler.id.instClass.setDefaultPartitioning(self.handler.id, doClear = 0) if self.encrypted: - self.handler.id.partitions.autoEncrypt = True - self.handler.id.partitions.encryptionPassphrase = self.passphrase + self.handler.id.storage.autoEncrypt = True + self.handler.id.storage.encryptionPassphrase = self.passphrase self.handler.skipSteps.extend(["partition", "zfcpconfig", "parttype"]) return retval @@ -181,7 +179,11 @@ class Bootloader(commands.bootloader.F8_Bootloader): self.handler.id.bootloader.doUpgradeOnly = 1 if self.driveorder: - hds = isys.hardDriveDict().keys() + # XXX I don't like that we are supposed to have scanned the + # storage devices already and yet we cannot know about + # ignoredDisks, exclusiveDisks, or iscsi disks before we + # have processed the kickstart config file. + hds = [d.name for d in self.handler.id.storage.disks] for disk in self.driveorder: if disk not in hds: raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in driveorder command" % disk) @@ -238,10 +240,10 @@ class ClearPart(commands.clearpart.FC3_ClearPart): if disk not in hds: raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in clearpart command" % disk) - self.handler.id.partitions.autoClearPartType = self.type - self.handler.id.partitions.autoClearPartDrives = self.drives + self.handler.id.storage.autoClearPartType = self.type + self.handler.id.storage.autoClearPartDrives = self.drives if self.initAll: - self.handler.id.partitions.reinitializeDisks = self.initAll + self.handler.id.storage.reinitializeDisks = self.initAll return retval @@ -269,15 +271,13 @@ class IgnoreDisk(commands.ignoredisk.F8_IgnoreDisk): def parse(self, args): retval = commands.ignoredisk.F8_IgnoreDisk.parse(self, args) - diskset = self.handler.id.diskset for drive in self.ignoredisk: - if not drive in diskset.skippedDisks: - diskset.skippedDisks.append(drive) + if not drive in self.handler.id.storage.ignoredDisks: + self.handler.id.storage.ignoredDisks.append(drive) - diskset = self.handler.id.diskset for drive in self.onlyuse: - if not drive in diskset.exclusiveDisks: - diskset.exclusiveDisks.append(drive) + if not drive in self.handler.id.storage.exclusiveDisks: + self.handler.id.storage.exclusiveDisks.append(drive) return retval @@ -304,8 +304,6 @@ class Iscsi(commands.iscsi.F10_Iscsi): if self.handler.id.iscsi.addTarget(**kwargs): log.info("added iscsi target: %s" %(target.ipaddr,)) - # FIXME: flush the drive dict so we figure drives out again - isys.flushDriveDict() return retval class IscsiName(commands.iscsiname.FC6_IscsiName): @@ -359,7 +357,7 @@ class LogVol(commands.logvol.F9_LogVol): except KeyError: raise KickstartValueError, formatErrorMsg(self.lineno, msg="No volume group exists with the name '%s'. Specify volume groups before logical volumes." % lvd.vgname) - for areq in self.handler.id.partitions.autoPartitionRequests: + for areq in self.handler.id.storage.autoPartitionRequests: if areq.type == REQUEST_LV: if areq.volumeGroup == vgid and areq.logicalVolumeName == lvd.name: raise KickstartValueError, formatErrorMsg(self.lineno, msg="Logical volume name already used in volume group %s" % lvd.vgname) @@ -395,8 +393,8 @@ class LogVol(commands.logvol.F9_LogVol): if lvd.encrypted: if lvd.passphrase and \ - not self.handler.anaconda.id.partitions.encryptionPassphrase: - self.handler.anaconda.id.partitions.encryptionPassphrase = lvd.passphrase + not self.handler.anaconda.id.storage.encryptionPassphrase: + self.handler.anaconda.id.storage.encryptionPassphrase = lvd.passphrase request.encryption = cryptodev.LUKSDevice(passphrase=lvd.passphrase, format=lvd.format) addPartRequest(self.handler.anaconda, request) @@ -635,7 +633,7 @@ class Partition(commands.partition.F9_Partition): request.uniqueID = uniqueID if pd.onPart != "": request.device = pd.onPart - for areq in self.handler.id.partitions.autoPartitionRequests: + for areq in self.handler.id.storage.autoPartitionRequests: if areq.device is not None and areq.device == pd.onPart: raise KickstartValueError, formatErrorMsg(self.lineno, "Partition already used") @@ -644,8 +642,8 @@ class Partition(commands.partition.F9_Partition): if pd.encrypted: if pd.passphrase and \ - not self.handler.anaconda.id.partitions.encryptionPassphrase: - self.handler.anaconda.id.partitions.encryptionPassphrase = pd.passphrase + not self.handler.anaconda.id.storage.encryptionPassphrase: + self.handler.anaconda.id.storage.encryptionPassphrase = pd.passphrase request.encryption = cryptodev.LUKSDevice(passphrase=pd.passphrase, format=pd.format) addPartRequest(self.handler.anaconda, request) @@ -728,8 +726,8 @@ class Raid(commands.raid.F9_Raid): if rd.encrypted: if rd.passphrase and \ - not self.handler.anaconda.id.partitions.encryptionPassphrase: - self.handler.anaconda.id.partitions.encryptionPassphrase = rd.passphrase + not self.handler.anaconda.id.storage.encryptionPassphrase: + self.handler.anaconda.id.storage.encryptionPassphrase = rd.passphrase request.encryption = cryptodev.LUKSDevice(passphrase=rd.passphrase, format=rd.format) addPartRequest(self.handler.anaconda, request) @@ -830,7 +828,7 @@ class XConfig(commands.xconfig.F10_XConfig): class ZeroMbr(commands.zerombr.FC3_ZeroMbr): def parse(self, args): retval = commands.zerombr.FC3_ZeroMbr.parse(self, args) - self.handler.id.partitions.zeroMbr = 1 + self.handler.id.storage.zeroMbr = 1 return retval class ZFCP(commands.zfcp.FC3_ZFCP): @@ -1002,14 +1000,14 @@ class AnacondaKSParser(KickstartParser): # else with this mountpoint so that you can use autopart and override / def addPartRequest(anaconda, request): if not request.mountpoint: - anaconda.id.partitions.autoPartitionRequests.append(request) + anaconda.id.storage.autoPartitionRequests.append(request) return - for req in anaconda.id.partitions.autoPartitionRequests: + for req in anaconda.id.storage.autoPartitionRequests: if req.mountpoint and req.mountpoint == request.mountpoint: - anaconda.id.partitions.autoPartitionRequests.remove(req) + anaconda.id.storage.autoPartitionRequests.remove(req) break - anaconda.id.partitions.autoPartitionRequests.append(request) + anaconda.id.storage.autoPartitionRequests.append(request) def processKickstartFile(anaconda, file): # make sure our disks are alive diff --git a/livecd.py b/livecd.py index 5d7d3f7b7..695f241d6 100644 --- a/livecd.py +++ b/livecd.py @@ -41,7 +41,6 @@ _ = lambda x: gettext.ldgettext("anaconda", x) import backend import isys import iutil -import fsset import packages @@ -174,16 +173,9 @@ class LiveCDCopyBackend(backend.AnacondaBackend): osimg = self._getLiveBlockDevice() # the real image osfd = os.open(osimg, os.O_RDONLY) - r = anaconda.id.fsset.getEntryByMountPoint("/") - rootfs = r.device.setupDevice() - rootfd = os.open(rootfs, os.O_WRONLY) - - # set the rootfs to have the right type. this lets things work - # given ext2 or ext3 (and in the future, ext4) - # FIXME: should we try to migrate if there isn't a match? - roottype = isys.readFSType(osimg) - if roottype is not None: - r.fsystem = fsset.fileSystemTypeGet(roottype) + rootDevice = anaconda.id.fsset.rootDevice + rootDevice.setup() + rootfd = os.open(rootDevice.path, os.O_WRONLY) readamt = 1024 * 1024 * 8 # 8 megs at a time size = self._getLiveSize() @@ -222,6 +214,7 @@ class LiveCDCopyBackend(backend.AnacondaBackend): anaconda.id.instProgress = None def _doFilesystemMangling(self, anaconda): + # FIXME: this whole method is a big fucking mess log.info("doing post-install fs mangling") wait = anaconda.intf.waitWindow(_("Doing post-installation"), _("Performing post-installation filesystem changes. This may take several minutes...")) @@ -233,12 +226,16 @@ class LiveCDCopyBackend(backend.AnacondaBackend): anaconda.id.fsset.mountFilesystems(anaconda) # restore the label of / to what we think it is - r = anaconda.id.fsset.getEntryByMountPoint("/") - anaconda.id.fsset.labelEntry(r, anaconda.rootPath, True) + rootDevice = anaconda.id.fsset.rootDevice + rootDevice.setup() # ensure we have a random UUID on the rootfs # FIXME: this should be abstracted per filesystem type - iutil.execWithRedirect("tune2fs", ["-U", "random", "/dev/%s" % (r.device.getDevice())], - stdout="/dev/tty5", stderr="/dev/tty5", + iutil.execWithRedirect("tune2fs", + ["-U", + "random", + rootDevice.path], + stdout="/dev/tty5", + stderr="/dev/tty5", searchPath = 1) # for any filesystem that's _not_ on the root, we need to handle @@ -246,6 +243,7 @@ class LiveCDCopyBackend(backend.AnacondaBackend): # this is pretty distasteful, but should work with things like # having a separate /usr/local + # XXX wow, what in the hell is going on here? # get a list of fsset entries that are relevant entries = sorted(filter(lambda e: not e.fsystem.isKernelFS() and \ e.getMountPoint(), anaconda.id.fsset.entries)) @@ -314,13 +312,12 @@ class LiveCDCopyBackend(backend.AnacondaBackend): def _resizeRootfs(self, anaconda, win = None): log.info("going to do resize") - r = anaconda.id.fsset.getEntryByMountPoint("/") - rootdev = r.device.getDevice() + rootDevice = anaconda.id.fsset.rootDevice # FIXME: we'd like to have progress here to give an idea of # how long it will take. or at least, to give an indefinite # progress window. but, not for this time - cmd = ["resize2fs", "/dev/%s" %(rootdev,), "-p"] + cmd = ["resize2fs", rootDevice.path, "-p"] out = open("/dev/tty5", "w") proc = subprocess.Popen(cmd, stdout=out, stderr=out) rc = proc.poll() @@ -376,9 +373,8 @@ class LiveCDCopyBackend(backend.AnacondaBackend): # FIXME: really, this should be in the general sanity checking, but # trying to weave that in is a little tricky at present. ossize = self._getLiveSizeMB() - slash = anaconda.id.partitions.getRequestByMountPoint("/") - if slash and \ - slash.getActualSize(anaconda.id.partitions, anaconda.id.diskset) < ossize: + slash = anaconda.id.fsset.rootDevice + if slash.size < ossize: rc = anaconda.intf.messageWindow(_("Error"), _("The root filesystem you created is " "not large enough for this live " @@ -391,7 +387,6 @@ class LiveCDCopyBackend(backend.AnacondaBackend): return DISPATCH_BACK else: sys.exit(1) - # package/group selection doesn't apply for this backend def groupExists(self, group): diff --git a/network.py b/network.py index af6d858f7..3531de999 100644 --- a/network.py +++ b/network.py @@ -585,8 +585,8 @@ class Network: # tell NM not to touch the interface(s) actually used for /, but we # have no logic to determine that if anaconda is not None: - rootdev = anaconda.id.fsset.getEntryByMountPoint("/").device - if rootdev.isNetdev(): + rootdev = anaconda.id.storage.fsset.rootDevice + if rootdev.isNetdev: f.write("NM_CONTROLLED=no\n") f.close() diff --git a/packages.py b/packages.py index f871c49f8..b34725a73 100644 --- a/packages.py +++ b/packages.py @@ -107,19 +107,6 @@ def doMigrateFilesystems(anaconda): bindMountDevDirectory(anaconda.rootPath) def turnOnFilesystems(anaconda): - def handleResizeError(e, dev): - if os.path.exists("/tmp/resize.out"): - details = open("/tmp/resize.out", "r").read() - else: - details = "%s" %(e,) - anaconda.intf.detailedMessageWindow(_("Resizing Failed"), - _("There was an error encountered " - "resizing the device %s.") %(dev,), - details, - type = "custom", - custom_buttons = [_("_Exit installer")]) - sys.exit(1) - if anaconda.dir == DISPATCH_BACK: log.info("unmounting filesystems") anaconda.id.fsset.umountFilesystems(anaconda.rootPath) @@ -133,53 +120,87 @@ def turnOnFilesystems(anaconda): iutil.execWithRedirect("swapoff", ["-a"], stdout = "/dev/tty5", stderr="/dev/tty5", searchPath = 1) - anaconda.id.partitions.doMetaDeletes(anaconda.id.diskset) - anaconda.id.fsset.setActive(anaconda.id.diskset, anaconda.id.partitions.requests) - try: - anaconda.id.fsset.shrinkFilesystems(anaconda.id.diskset, anaconda.rootPath) - except fsset.ResizeError, (e, dev): - handleResizeError(e, dev) - if not anaconda.id.fsset.isActive(): - anaconda.id.diskset.savePartitions () - # this is somewhat lame, but we seem to be racing with - # device node creation sometimes. so wait for device nodes - # to settle - time.sleep(1) - w = anaconda.intf.waitWindow(_("Activating"), _("Activating new partitions. Please wait...")) - rc = iutil.execWithRedirect("/sbin/udevadm", [ "settle" ], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - searchPath = 1) - w.pop() - - anaconda.id.partitions.doEncryptionRetrofits() - - try: - anaconda.id.partitions.doMetaResizes(anaconda.id.diskset) - except lvm.LVResizeError, e: - handleResizeError("%s" %(e,), "%s/%s" %(e.vgname, e.lvname)) try: - anaconda.id.fsset.growFilesystems(anaconda.id.diskset, anaconda.rootPath) - except fsset.ResizeError, (e, dev): - handleResizeError(e, dev) - - if not anaconda.id.fsset.volumesCreated: - try: - anaconda.id.fsset.createLogicalVolumes(anaconda.rootPath) - except SystemError, e: - log.error("createLogicalVolumes failed with %s", str(e)) - anaconda.intf.messageWindow(_("LVM operation failed"), - str(e)+"\n\n"+_("The installer will now exit..."), - type="custom", custom_icon="error", custom_buttons=[_("_Reboot")]) - sys.exit(0) - - anaconda.id.fsset.formatSwap(anaconda.rootPath) - anaconda.id.fsset.turnOnSwap(anaconda.rootPath) - anaconda.id.fsset.makeFilesystems(anaconda.rootPath, - anaconda.backend.skipFormatRoot) - anaconda.id.fsset.mountFilesystems(anaconda,0,0, - anaconda.backend.skipFormatRoot) + anaconda.id.storage.processActions(anaconda.intf) + except DeviceResizeError as (msg, device): + # XXX does this make any sense? do we support resize of + # devices other than partitions? + anaconda.intf.detailedMessageWindow(_("Device Resize Failed"), + _("An error was encountered while " + "resizing device %s.") % (device,), + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except DeviceCreateError as (msg, device): + anaconda.intf.detailedMessageWindow(_("Device Creation Failed"), + _("An error was encountered while " + "creating device %s.") % (device,), + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except DeviceDestroyError as (msg, device): + anaconda.intf.detailedMessageWindow(_("Device Removal Failed"), + _("An error was encountered while " + "removing device %s.") % (device,), + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except DeviceError as (msg, device): + anaconda.intf.detailedMessageWindow(_("Device Setup Failed"), + _("An error was encountered while " + "setting up device %s.") % (device,), + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except FSResizeError as (msg, device): + if os.path.exists("/tmp/resize.out"): + details = open("/tmp/resize.out", "r").read() + else: + details = "%s" %(msg,) + anaconda.intf.detailedMessageWindow(_("Resizing Failed"), + _("There was an error encountered while " + "resizing the device %s.") %(device,), + details, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except FSMigrateError as (msg, device): + anaconda.intf.detailedMessageWindow(_("Migration Failed"), + _("An error was encountered while " + "migrating filesystem on device %s.") + % (device,), + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except FormatCreateError as (msg, device): + anaconda.intf.detailedMessageWindow(_("Formatting Failed"), + _("An error was encountered while " + "formatting device %s.") % (device,), + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except Exception as msg: + # catch-all + anaconda.intf.detailedMessageWindow(_("Storage Activation Failed"), + _("An error was encountered while " + "activating your storage configuration.") + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + + anaconda.id.fsset.turnOnSwap(anaconda.intf) + anaconda.id.storage.fsset.mountFilesystems(anaconda, + raiseErrors=False, + readOnly=False, + skipRoot=anaconda.backend.skipFormatRoot) def setupTimezone(anaconda): # we don't need this on an upgrade or going backwards diff --git a/rescue.py b/rescue.py index 42e5b960c..3006971a8 100644 --- a/rescue.py +++ b/rescue.py @@ -29,8 +29,8 @@ from flags import flags import sys import os import isys +from storage import mountExistingSystem import iutil -import fsset import shutil import time import network @@ -101,14 +101,17 @@ class RescueInterface: # XXX grub-install is stupid and uses df output to figure out # things when installing grub. make /etc/mtab be at least # moderately useful. -def makeMtab(instPath, theFsset): - child = os.fork() - if (not child): - os.chroot(instPath) +def makeMtab(instPath, fsset): + try: f = open("/etc/mtab", "w+") - f.write(theFsset.mtab()) + except IOError, e: + log.info("failed to open /etc/mtab for write: %s" % e) + return + + try: + f.write(fsset.mtab()) + finally: f.close() - os._exit(0) # make sure they have a resolv.conf in the chroot def makeResolvConf(instPath): @@ -265,17 +268,14 @@ def runRescue(anaconda, instClass): else: scroll = 0 - partList = [] - for (drive, fs, relstr, label, uuid) in disks: - if label: - partList.append("%s (%s)" % (drive, label)) - else: - partList.append(drive) + devList = [] + for (device, relstr) in disks: + devList.append(device.path) (button, choice) = \ ListboxChoiceWindow(screen, _("System to Rescue"), - _("What partition holds the root partition " - "of your installation?"), partList, + _("Which device holds the root partition " + "of your installation?"), devList, [ _("OK"), _("Exit") ], width = 30, scroll = scroll, height = height, help = "multipleroot") @@ -283,19 +283,15 @@ def runRescue(anaconda, instClass): if button == string.lower (_("Exit")): root = None else: - root = disks[choice] + root = disks[choice][0] rootmounted = 0 if root: try: - fs = fsset.FileSystemSet() - - # only pass first two parts of tuple for root, since third - # element is a comment we dont want - rc = upgrade.mountRootPartition(anaconda, root[:2], fs, - allowDirty = 1, warnDirty = 1, - readOnly = readOnly) + rc = mountExistingSystem(anaconda, root, + allowDirty = 1, warnDirty = 1, + readOnly = readOnly) if rc == -1: if anaconda.isKickstart: @@ -325,7 +321,7 @@ def runRescue(anaconda, instClass): # now turn on swap if not readOnly: try: - fs.turnOnSwap("/") + anaconda.id.storage.fsset.turnOnSwap(anaconda.intf) except: log.error("Error enabling swap") @@ -412,7 +408,7 @@ def runRescue(anaconda, instClass): msgStr = "" if rootmounted and not readOnly: - makeMtab(anaconda.rootPath, fs) + makeMtab(anaconda.rootPath, anaconda.id.storage.fsset) try: makeResolvConf(anaconda.rootPath) except Exception, e: diff --git a/upgrade.py b/upgrade.py index a554d3bbe..98979b836 100644 --- a/upgrade.py +++ b/upgrade.py @@ -26,14 +26,13 @@ import iutil import time import sys import os.path -import partedUtils import string import selinux -import lvm from flags import flags -from fsset import * from constants import * from product import productName +from storage import findExistingRootDevices, FSSet +from storage.formats import getFormat import rhpl import rhpl.arch @@ -134,11 +133,8 @@ def findRootParts(anaconda): root_device=anaconda.id.ksdata.upgrade.root_device anaconda.id.upgradeRoot = [] - for (dev, fs, meta, label, uuid) in anaconda.id.rootParts: - if (root_device is not None) and ((dev == root_device) or (("UUID=%s" % uuid) == root_device) or (("LABEL=%s" % label) == root_device)): - anaconda.id.upgradeRoot.append( (dev, fs) ) - else: - anaconda.id.upgradeRoot.append( (dev, fs) ) + for (dev, label) in anaconda.id.rootParts: + anaconda.id.upgradeRoot.append( (dev, fs) ) if anaconda.id.rootParts is not None and len(anaconda.id.rootParts) > 0: anaconda.dispatch.skipStep("findinstall", skip = 0) @@ -149,111 +145,19 @@ def findRootParts(anaconda): anaconda.dispatch.skipStep("installtype", skip = 0) def findExistingRoots(anaconda, upgradeany = 0): - # make ibft configured iscsi disks available - anaconda.id.iscsi.startup(anaconda.intf) - if not flags.setupFilesystems: (prod, ver) = partedUtils.getReleaseString (anaconda.rootPath) if flags.cmdline.has_key("upgradeany") or upgradeany == 1 or anaconda.id.instClass.productUpgradable(prod, ver): - return [(anaconda.rootPath, 'ext2', "")] + return [(anaconda.rootPath, "")] return [] - anaconda.id.diskset.openDevices() - anaconda.id.partitions.getEncryptedDevices(anaconda.id.diskset) - rootparts = anaconda.id.diskset.findExistingRootPartitions(upgradeany = upgradeany) - - # close the devices to make sure we don't leave things sitting open - anaconda.id.diskset.closeDevices() - - # this is a hack... need to clear the skipped disk list after this - partedUtils.DiskSet.skippedDisks = [] - partedUtils.DiskSet.exclusiveDisks = [] - return rootparts -def getDirtyDevString(dirtyDevs): - ret = "" - for dev in dirtyDevs: - if dev != "loop": - ret = "/dev/%s\n" % (dev,) - else: - ret = "%s\n" % (dev,) - return ret - -def mountRootPartition(anaconda, rootInfo, oldfsset, allowDirty = 0, - warnDirty = 0, readOnly = 0): - (root, rootFs) = rootInfo - bindMount = 0 - - encryptedDevices = anaconda.id.partitions.encryptedDevices - diskset = partedUtils.DiskSet(anaconda) - diskset.openDevices() - for cryptoDev in encryptedDevices.values(): - cryptoDev.openDevice() - diskset.startMPath() - diskset.startDmRaid() - diskset.startMdRaid() - for cryptoDev in encryptedDevices.values(): - cryptoDev.openDevice() - lvm.vgscan() - lvm.vgactivate() - for cryptoDev in encryptedDevices.values(): - cryptoDev.openDevice() - - if root in anaconda.id.partitions.protectedPartitions() and os.path.ismount("/mnt/isodir"): - root = "/mnt/isodir" - bindMount = 1 - - log.info("going to mount %s on %s as %s" %(root, anaconda.rootPath, rootFs)) - isys.mount(root, anaconda.rootPath, rootFs, bindMount=bindMount) - - oldfsset.reset() - newfsset = readFstab(anaconda) - - for entry in newfsset.entries: - oldfsset.add(entry) - - if not bindMount: - isys.umount(anaconda.rootPath) - - dirtyDevs = oldfsset.hasDirtyFilesystems(anaconda.rootPath) - if not allowDirty and dirtyDevs != []: - lvm.vgdeactivate() - diskset.stopMdRaid() - diskset.stopDmRaid() - diskset.stopMPath() - anaconda.intf.messageWindow(_("Dirty File Systems"), - _("The following file systems for your Linux system " - "were not unmounted cleanly. Please boot your " - "Linux installation, let the file systems be " - "checked and shut down cleanly to upgrade.\n" - "%s" %(getDirtyDevString(dirtyDevs),))) - sys.exit(0) - elif warnDirty and dirtyDevs != []: - rc = anaconda.intf.messageWindow(_("Dirty File Systems"), - _("The following file systems for your Linux " - "system were not unmounted cleanly. Would " - "you like to mount them anyway?\n" - "%s" % (getDirtyDevString(dirtyDevs,))), - type = "yesno") - if rc == 0: - return -1 - - if flags.setupFilesystems: - for dev, crypto in encryptedDevices.items(): - if crypto.openDevice(): - log.error("failed to open encrypted device %s" % (dev,)) - - oldfsset.mountFilesystems(anaconda, readOnly = readOnly, - skiprootfs = bindMount) - - rootEntry = oldfsset.getEntryByMountPoint("/") - if (not rootEntry or not rootEntry.fsystem or not rootEntry.fsystem.isMountable()): - raise RuntimeError, "/etc/fstab did not list a fstype for the root partition which we support" - def bindMountDevDirectory(instPath): - fs = fileSystemTypeGet("bind") - fs.mount("/dev", "/dev", bindMount=1, instroot=instPath) + getFormat("bind", + device="/dev", + mountpoint="/dev", + exists=True).mount(chroot=instPath) # returns None if no filesystem exist to migrate def upgradeMigrateFind(anaconda): @@ -295,13 +199,15 @@ def upgradeSwapSuggestion(anaconda): fsList = [] - for entry in anaconda.id.fsset.entries: - if entry.fsystem.getName() in getUsableLinuxFs(): - if flags.setupFilesystems and not entry.isMounted(): + for device in anaconda.id.fsset.devices: + if not device.format: + continue + if device.format.mountable and device.format.linuxNative: + if flags.setupFilesystems and not device.format.status: continue - space = isys.pathSpaceAvailable(anaconda.rootPath + entry.mountpoint) + space = isys.pathSpaceAvailable(anaconda.rootPath + device.format.mountpoint) if space > 16: - info = (entry.mountpoint, entry.device.getDevice(), space) + info = (device, space) fsList.append(info) suggestion = mem * 2 - swap @@ -311,57 +217,11 @@ def upgradeSwapSuggestion(anaconda): suggestion = 32 suggSize = 0 suggMnt = None - for (mnt, part, size) in fsList: + for (device, size) in fsList: if (size > suggSize) and (size > (suggestion + 100)): - suggMnt = mnt - - anaconda.id.upgradeSwapInfo = (fsList, suggestion, suggMnt) - -def swapfileExists(swapname): - try: - os.lstat(swapname) - return 1 - except: - return 0 + suggDev = device -def createSwapFile(instPath, theFsset, mntPoint, size): - fstabPath = instPath + "/etc/fstab" - prefix = "" - - if mntPoint != "/": - file = mntPoint + "/SWAP" - else: - file = "/SWAP" - - swapFileDict = {} - for entry in theFsset.entries: - if entry.fsystem.getName() == "swap": - swapFileDict[entry.device.getName()] = 1 - - count = 0 - while (swapfileExists(instPath + file) or - swapFileDict.has_key(file)): - count = count + 1 - tmpFile = "/SWAP-%d" % (count) - if mntPoint != "/": - file = mntPoint + tmpFile - else: - file = tmpFile - - device = SwapFileDevice(file) - device.setSize(size) - fsystem = fileSystemTypeGet("swap") - entry = FileSystemSetEntry(device, "swap", fsystem) - entry.setFormat(1) - theFsset.add(entry) - theFsset.formatEntry(entry, instPath) - theFsset.turnOnSwap(instPath, upgrading=True) - - # XXX generalize fstab modification - f = open(fstabPath, "a") - format = "%-23s %-23s %-7s %-15s %d %d\n"; - f.write(format % (prefix + file, "swap", "swap", "defaults", 0, 0)) - f.close() + anaconda.id.upgradeSwapInfo = (fsList, suggestion, suggDev) # XXX handle going backwards def upgradeMountFilesystems(anaconda): @@ -369,21 +229,15 @@ def upgradeMountFilesystems(anaconda): if flags.setupFilesystems: try: - mountRootPartition(anaconda, anaconda.id.upgradeRoot[0], anaconda.id.fsset, - allowDirty = 0) - except SystemError: + mountExistingSystem(anaconda, + anaconda.id.upgradeRoot[0], + allowDirty = 0) + except Exception: anaconda.intf.messageWindow(_("Mount failed"), _("One or more of the file systems listed in the " "/etc/fstab on your Linux system cannot be mounted. " "Please fix this problem and try to upgrade again.")) sys.exit(0) - except RuntimeError: - anaconda.intf.messageWindow(_("Mount failed"), - _("One or more of the file systems listed in the " - "/etc/fstab of your Linux system are inconsistent and " - "cannot be mounted. Please fix this problem and try to " - "upgrade again.")) - sys.exit(0) checkLinks = ( '/etc', '/var', '/var/lib', '/var/lib/rpm', '/boot', '/tmp', '/var/tmp', '/root', @@ -430,15 +284,11 @@ def upgradeMountFilesystems(anaconda): % (anaconda.rootPath + "/etc/fstab",), type="ok") return DISPATCH_BACK - - newfsset = readFstab(anaconda) - for entry in newfsset.entries: - anaconda.id.fsset.add(entry) + + anaconda.id.storage.fsset.parseFSTab(chroot=anaconda.rootPath) if flags.setupFilesystems: - if iutil.isPPC(): - anaconda.id.fsset.formatSwap(anaconda.rootPath, forceFormat=True) - anaconda.id.fsset.turnOnSwap(anaconda.rootPath, upgrading=True) - anaconda.id.fsset.mkDevRoot(anaconda.rootPath) + anaconda.id.storage.fsset.turnOnSwap(upgrading=True) + anaconda.id.storage.fsset.mkDevRoot(anaconda.rootPath) # if they've been booting with selinux disabled, then we should # disable it during the install as well (#242510) @@ -458,9 +308,9 @@ def setSteps(anaconda): "keyboard", "welcome", "installtype", + "storageinit", "findrootparts", "findinstall", - "partitionobjinit", "upgrademount", "upgrademigfind", "upgrademigratefs", @@ -473,7 +323,6 @@ def setSteps(anaconda): "confirmupgrade", "postselection", "install", - "migratefilesystems", "preinstallconfig", "installpackages", "postinstallconfig", diff --git a/yuminstall.py b/yuminstall.py index 2d63dac5d..68fd5a191 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -1293,26 +1293,18 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon elif iutil.isIA64(): self.selectPackage("elilo") - def selectFSPackages(self, fsset, diskset): - for entry in fsset.entries: - map(self.selectPackage, entry.fsystem.getNeededPackages()) - if entry.device.crypto: - self.selectPackage("cryptsetup-luks") - - for disk in diskset.disks.keys(): - if isys.driveIsIscsi(disk): + def selectFSPackages(self, storage): + for device in storage.fsset.devices: + # this takes care of device and filesystem packages + map(self.selectPackage, device.packages) + + for disk in storage.disks: + # FIXME: this should get handled by the above + if isys.driveIsIscsi(disk.path): log.info("ensuring iscsi is installed") self.selectPackage("iscsi-initiator-utils") break - if diskset.__class__.mpList: - log.info("ensuring device-mapper-multipath is installed") - self.selectPackage("device-mapper-multipath") - if diskset.__class__.dmList: - log.info("ensuring dmraid is installed") - self.selectPackage("dmraid") - - # anaconda requires several programs on the installed system to complete # installation, but we have no guarantees that some of these will be # installed (they could have been removed in kickstart). So we'll force @@ -1335,7 +1327,7 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon # New installs only - upgrades will already have all this stuff. self.selectBestKernel(anaconda) self.selectBootloader() - self.selectFSPackages(anaconda.id.fsset, anaconda.id.diskset) + self.selectFSPackages(anaconda.id.storage) self.selectAnacondaNeeds() if anaconda.id.getUpgrade(): -- cgit From b2dedb483bd9514cce023c284463d18fc0f7b612 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 13:14:27 -0600 Subject: Update UI code to use new storage module. --- cmdline.py | 4 - gui.py | 6 +- iw/lvm_dialog_gui.py | 692 ++++++++++++++++++++--------------------- iw/partition_dialog_gui.py | 410 ++++++++++-------------- iw/partition_gui.py | 640 +++++++++++++++++-------------------- iw/partition_ui_helpers_gui.py | 95 +++--- iw/raid_dialog_gui.py | 315 +++++++++---------- partIntfHelpers.py | 427 +++++-------------------- text.py | 4 - 9 files changed, 1079 insertions(+), 1514 deletions(-) diff --git a/cmdline.py b/cmdline.py index 7b925c9cf..f87550c46 100644 --- a/cmdline.py +++ b/cmdline.py @@ -114,10 +114,6 @@ class InstallInterface: pass def run(self, anaconda): - anaconda.id.fsset.registerMessageWindow(self.messageWindow) - anaconda.id.fsset.registerProgressWindow(self.progressWindow) - anaconda.id.fsset.registerWaitWindow(self.waitWindow) - (step, instance) = anaconda.dispatch.currentStep() while step: if stepToClasses.has_key(step): diff --git a/gui.py b/gui.py index 14ef0450e..d224fbd0b 100755 --- a/gui.py +++ b/gui.py @@ -752,7 +752,7 @@ class SaveExceptionWindow: elif len(dests) > 0: for d in dests: iter = store.append(None) - store[iter] = ("/dev/%s" % d[0], "/dev/%s - %s" % (d[0], d[1])) + store[iter] = (d[0], "%s - %s" % (d[0], d[1])) self.diskCombo.set_model(store) self.diskCombo.set_active(0) @@ -1267,10 +1267,6 @@ class InstallInterface: if anaconda.id.keyboard and not anaconda.id.x_already_set: anaconda.id.keyboard.activate() - anaconda.id.fsset.registerMessageWindow(self.messageWindow) - anaconda.id.fsset.registerProgressWindow(self.progressWindow) - anaconda.id.fsset.registerWaitWindow(self.waitWindow) - self.icw = InstallControlWindow (self.anaconda) self.icw.run (self.runres) diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index 25c0e6dda..867755ab0 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -1,5 +1,5 @@ # -# lvm_dialog_gui.py: dialog for editting a volume group request +# lvm_dialog_gui.py: dialog for editing a volume group request # # Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. # All rights reserved. @@ -27,12 +27,9 @@ import gtk import datacombo import gui -from fsset import * -from partRequests import * from partition_ui_helpers_gui import * from constants import * import lvm -from cryptodev import LUKSDevice import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -46,8 +43,9 @@ class VolumeGroupEditor: return max(0, lvm.MAX_LV_SLOTS - len(self.logvolreqs)) def computeSpaceValues(self, alt_pvlist=None, usepe=None): + # FIXME: stop using this in favor of the vg's methods if usepe is None: - pesize = long(self.peCombo.get_active_value()) + pesize = long(self.peCombo.get_active_value()) / 1024.0 else: pesize = long(usepe) @@ -55,11 +53,13 @@ class VolumeGroupEditor: pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model()) else: pvlist = alt_pvlist - tspace = self.computeVGSize(pvlist, pesize) - uspace = self.computeLVSpaceNeeded(self.logvolreqs) - fspace = tspace - uspace - return (tspace, uspace, fspace) + # XXX this should all be computed using self.vg + vgsize = self.computeVGSize(pvlist, pesize) + vgused = self.computeLVSpaceNeeded(self.vg.lvs) + vgfree = vgsize - vgused + + return (vgsize, vgused, vgfree) def getPVWastedRatio(self, newpe): """ given a new pe value, return percentage of smallest PV wasted @@ -69,10 +69,8 @@ class VolumeGroupEditor: pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model()) waste = 0.0 - for id in pvlist: - pvreq = self.partitions.getRequestByID(id) - pvsize = pvreq.getActualSize(self.partitions, self.diskset) - waste = max(waste, (long(pvsize*1024) % newpe)/(pvsize*1024.0)) + for pv in pvlist: + waste = max(waste, (long(pv.size*1024) % newpe)/(pv.size*1024.0)) return waste @@ -111,10 +109,10 @@ class VolumeGroupEditor: oldused = 0 used = 0 resize = 0 - for lv in self.logvolreqs: - osize = lv.getActualSize(self.partitions, self.diskset, True) + for lv in self.vg.lvs: + osize = lv.size oldused = oldused + osize - nsize = lvm.clampLVSizeRequest(osize, newpe, roundup=1) + nsize = lvm.clampSize(osize, newpe, roundup=1) if nsize != osize: resize = 1 @@ -144,11 +142,13 @@ class VolumeGroupEditor: custom_buttons=["gtk-cancel", _("C_ontinue")]) if not rc: return 0 - + + # XXX this is very sneaky, just changing the lvs' size attributes + # will we suffer for it? almost certainly. for lv in self.logvolreqs: - osize = lv.getActualSize(self.partitions, self.diskset, True) - nsize = lvm.clampLVSizeRequest(osize, newpe, roundup=1) - lv.setSize(nsize) + osize = lv.size + nsize = lvm.clampSize(osize, newpe, roundup=1) + lv.size = nsize return 1 @@ -161,24 +161,26 @@ class VolumeGroupEditor: """ curval = int(widget.get_active_value()) + # this one's in MB so we can stop with all this dividing by 1024 + curpe = curval / 1024.0 lastval = widget.get_data("lastpe") lastidx = widget.get_data("lastidx") # see if PE is too large compared to smallest PV - # remember PE is in KB, PV size is in MB maxpvsize = self.getSmallestPVSize() - if curval > maxpvsize * 1024: + if curpe > maxpvsize: self.intf.messageWindow(_("Not enough space"), _("The physical extent size cannot be " "changed because the value selected " "(%10.2f MB) is larger than the smallest " "physical volume (%10.2f MB) in the " - "volume group.") % (curval/1024.0, maxpvsize), custom_icon="error") + "volume group.") % (curpe, maxpvsize), + custom_icon="error") widget.set_active(lastidx) return 0 # see if new PE will make any PV useless due to overhead - if lvm.clampPVSize(maxpvsize, curval) * 1024 < curval: + if lvm.clampPVSize(maxpvsize, curpe) < curpe: self.intf.messageWindow(_("Not enough space"), _("The physical extent size cannot be " "changed because the value selected " @@ -186,14 +188,14 @@ class VolumeGroupEditor: "to the size of the " "smallest physical volume " "(%10.2f MB) in the " - "volume group.") % (curval/1024.0, + "volume group.") % (curpe, maxpvsize), custom_icon="error") widget.set_active(lastidx) return 0 - if self.getPVWastedRatio(curval) > 0.10: + if self.getPVWastedRatio(curpe) > 0.10: rc = self.intf.messageWindow(_("Too small"), _("This change in the value of the " "physical extent will waste " @@ -215,10 +217,9 @@ class VolumeGroupEditor: else: self.updateLogVolStore() else: - maxlv = lvm.getMaxLVSize(curval) - for lv in self.logvolreqs: - lvsize = lv.getActualSize(self.partitions, self.diskset, True) - if lvsize > maxlv: + maxlv = lvm.getMaxLVSize(curpe) + for lv in self.vg.lvs: + if lv.size > maxlv: self.intf.messageWindow(_("Not enough space"), _("The physical extent size " "cannot be changed because the " @@ -234,12 +235,15 @@ class VolumeGroupEditor: widget.set_data("lastpe", curval) widget.set_data("lastidx", widget.get_active()) + + # XXX this is pretty sneaky, too + self.vg.peSize = curpe self.updateAllowedLvmPartitionsList(self.availlvmparts, - self.partitions, self.lvmlist) self.updateVGSpaceLabels() def prettyFormatPESize(self, val): + """ Pretty print for PE size in KB """ if val < 1024: return "%s KB" % (val,) elif val < 1024*1024: @@ -285,11 +289,12 @@ class VolumeGroupEditor: # changes the toggle state val = not model.get_value(iter, 0) partname = model.get_value(iter, 1) - id = self.partitions.getRequestByDeviceName(partname).uniqueID + pv = self.storage.devicetree.getDeviceByName(partname) if val: - pvlist.append(id) + pvlist.append(pv) else: - pvlist.remove(id) + pvlist.remove(pv) + (availSpaceMB, neededSpaceMB, fspace) = self.computeSpaceValues(alt_pvlist=pvlist) if availSpaceMB < neededSpaceMB: @@ -301,12 +306,17 @@ class VolumeGroupEditor: "volumes."), custom_icon="error") return False + # XXX this is pretty sneaky + if val: + self.vg._addPV(pv) + else: + self.vg._removePV(pv) + self.updateVGSpaceLabels(alt_pvlist = pvlist) return True - def createAllowedLvmPartitionsList(self, alllvmparts, reqlvmpart, partitions, preexist = 0): - + def createAllowedLvmPartitionsList(self, alllvmparts, vgs, lvs): store = gtk.TreeStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_STRING) @@ -317,49 +327,50 @@ class VolumeGroupEditor: sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) sw.set_shadow_type(gtk.SHADOW_IN) - for part in alllvmparts: - uid = part[0] - request = partitions.getRequestByID(uid) + for device in alllvmparts: + # clip size to current PE + pesize = int(self.peCombo.get_active_value()) / 1024.0 + size = lvm.clampSize(size, pesize) + size_string = "%10.2f MB" % size + include = True + selected = False + + # now see if the pv is in use either by a vg in the tree or by + # the vg we are editing now + if device in self.vg.pvs: + selected = True + include = True + else: + for vg in vgs: + if vg.name == self.vg.name: + continue - if request.type != REQUEST_RAID: - partname = "%s" % (request.device,) - else: - partname = "md%d" % (request.raidminor,) + if device in vg.pvs: + include = False + break - size = request.getActualSize (partitions, self.diskset) - used = part[2] + if include and not self.vg.pvs: + selected = True - # clip size to current PE - pesize = int(self.peCombo.get_active_value()) - size = lvm.clampPVSize(size, pesize) - partsize = "%10.2f MB" % size - if used or not reqlvmpart: - selected = 1 - else: - selected = 0 - - if preexist == 0 or selected == 1: - partlist.append_row((partname, partsize), selected) + if include: + partlist.append_row((device.name, size_string), selected) return (partlist, sw) - def updateAllowedLvmPartitionsList(self, alllvmparts, partitions, partlist): + def updateAllowedLvmPartitionsList(self, alllvmparts, partlist): """ update sizes in pv list alllvmparts - list of pv from partitions.getAvailLVMPartitions - partitions - object holding all partition requests partlist - the checklist containing pv list """ row = 0 for part in alllvmparts: - uid = part[0] - request = partitions.getRequestByID(uid) - size = request.getActualSize(partitions, self.diskset) + size = part.size # clip size to current PE - pesize = int(self.peCombo.get_active_value()) - size = lvm.clampPVSize(size, pesize) + pesize = int(self.peCombo.get_active_value()) / 1024.0 + size = lvm.clampSize(size, pesize) partsize = "%10.2f MB" % size iter = partlist.store.get_iter((int(row),)) @@ -371,14 +382,16 @@ class VolumeGroupEditor: (model, iter) = selection.get_selected() return iter + def editLogicalVolume(self, lv, isNew = 0): + if not lv: + return - def editLogicalVolume(self, logrequest, isNew = 0): if isNew: tstr = _("Make Logical Volume") else: try: - tstr = _("Edit Logical Volume: %s") % (logrequest.logicalVolumeName,) - except: + tstr = _("Edit Logical Volume: %s") % (lv.name,) + except AttributeError: tstr = _("Edit Logical Volume") dialog = gtk.Dialog(tstr, self.parent) @@ -392,17 +405,24 @@ class VolumeGroupEditor: maintable.set_col_spacings(5) row = 0 + if lv.format.type == "luks": + usedev = self.storage.devicetree.getChildren(lv)[0] + format = usedev.format + elif lv: + usedev = lv + format = lv.format + lbl = createAlignedLabel(_("_Mount Point:")) maintable.attach(lbl, 0, 1, row,row+1) - mountCombo = createMountPointCombo(logrequest, excludeMountPoints=["/boot"]) + mountCombo = createMountPointCombo(usedev, excludeMountPoints=["/boot"]) lbl.set_mnemonic_widget(mountCombo) maintable.attach(mountCombo, 1, 2, row, row + 1) row += 1 - if not logrequest or not logrequest.getPreExisting(): + if not format.exists: lbl = createAlignedLabel(_("_File System Type:")) maintable.attach(lbl, 0, 1, row, row + 1) - newfstypeCombo = createFSTypeMenu(logrequest.fstype, + newfstypeCombo = createFSTypeMenu(format.name, fstypechangeCB, mountCombo, ignorefs = ["software RAID", "physical volume (LVM)", "vfat", "efi", "PPC PReP Boot", "Apple Bootstrap"]) @@ -412,77 +432,72 @@ class VolumeGroupEditor: else: maintable.attach(createAlignedLabel(_("Original File System Type:")), 0, 1, row, row + 1) - if logrequest.origfstype and logrequest.origfstype.getName(): - newfstypeCombo = gtk.Label(logrequest.origfstype.getName()) + if format.type: + newfstypeCombo = gtk.Label(format.name) else: newfstypeCombo = gtk.Label(_("Unknown")) maintable.attach(newfstypeCombo, 1, 2, row, row + 1) row += 1 - if logrequest.fslabel: + if getattr(format, "label", None): maintable.attach(createAlignedLabel(_("Original File System " "Label:")), 0, 1, row, row + 1) - maintable.attach(gtk.Label(logrequest.fslabel), 1, 2, row, - row + 1) + maintable.attach(gtk.Label(format.label), + 1, 2, row, row + 1) row += 1 - - if not logrequest or not logrequest.getPreExisting(): + if not lv.exists: lbl = createAlignedLabel(_("_Logical Volume Name:")) lvnameEntry = gtk.Entry(32) lbl.set_mnemonic_widget(lvnameEntry) - if logrequest and logrequest.logicalVolumeName: - lvnameEntry.set_text(logrequest.logicalVolumeName) + if lv and lv.lvname: + lvnameEntry.set_text(lv.lvname) else: - lvnameEntry.set_text(lvm.createSuggestedLVName(self.logvolreqs)) + lvnameEntry.set_text(storage.createSuggestedLVName(self.vg.lvs)) else: lbl = createAlignedLabel(_("Logical Volume Name:")) - lvnameEntry = gtk.Label(logrequest.logicalVolumeName) + lvnameEntry = gtk.Label(lv.lvname) maintable.attach(lbl, 0, 1, row, row + 1) maintable.attach(lvnameEntry, 1, 2, row, row + 1) row += 1 - if not logrequest or not logrequest.getPreExisting(): + if not lv.exists: lbl = createAlignedLabel(_("_Size (MB):")) sizeEntry = gtk.Entry(16) lbl.set_mnemonic_widget(sizeEntry) - if logrequest: - sizeEntry.set_text("%Ld" % (logrequest.getActualSize(self.partitions, self.diskset, True),)) + sizeEntry.set_text("%Ld" % lv.size) else: lbl = createAlignedLabel(_("Size (MB):")) - sizeEntry = gtk.Label(str(logrequest.size)) + sizeEntry = gtk.Label(str(lv.size)) maintable.attach(lbl, 0, 1, row, row+1) maintable.attach(sizeEntry, 1, 2, row, row + 1) row += 1 - if not logrequest or not logrequest.getPreExisting(): - pesize = int(self.peCombo.get_active_value()) - (tspace, uspace, fspace) = self.computeSpaceValues(usepe=pesize) - maxlv = min(lvm.getMaxLVSize(pesize), fspace) + if not lv.exists: + pesize = int(self.peCombo.get_active_value()) / 1024.0 + (total, used, free) = self.computeSpaceValues(usepe=pesize) + maxlv = min(lvm.getMaxLVSize(pesize), free) # add in size of current logical volume if it has a size - if logrequest and not isNew: - maxlv = maxlv + logrequest.getActualSize(self.partitions, self.diskset) + if lv and not isNew: + maxlv += lv.size + maxlabel = createAlignedLabel(_("(Max size is %s MB)") % (maxlv,)) maintable.attach(maxlabel, 1, 2, row, row + 1) self.fsoptionsDict = {} - if logrequest.getPreExisting(): - (row, self.fsoptionsDict) = createPreExistFSOptionSection(logrequest, maintable, row, mountCombo, self.partitions, ignorefs = ["software RAID", "physical volume (LVM)", "vfat"]) + if lv.exists: + (row, self.fsoptionsDict) = createPreExistFSOptionSection(usedev, maintable, row, mountCombo, self.storage, ignorefs = ["software RAID", "physical volume (LVM)", "vfat"]) # checkbutton for encryption using dm-crypt/LUKS - if not logrequest.getPreExisting(): + if not lv.exists: self.lukscb = gtk.CheckButton(_("_Encrypt")) - if logrequest.format or logrequest.type == REQUEST_NEW: - self.lukscb.set_data("formatstate", 1) - else: - self.lukscb.set_data("formatstate", 0) - - if logrequest.encryption: + self.lukscb.set_data("formatstate", 1) + if lv.format.type == "luks": self.lukscb.set_active(1) else: self.lukscb.set_active(0) @@ -500,79 +515,76 @@ class VolumeGroupEditor: dialog.destroy() return - if not logrequest or not logrequest.getPreExisting(): - fsystem = newfstypeCombo.get_active_value() - format = 1 - migrate = 0 - targetSize = None - else: - if self.fsoptionsDict.has_key("formatcb"): - format = self.fsoptionsDict["formatcb"].get_active() - if format: - fsystem = self.fsoptionsDict["fstypeCombo"].get_active_value() - else: - format = 0 + actions = [] + luksdev = None + targetSize = None + migrate_class = None + format = None - if self.fsoptionsDict.has_key("migratecb"): - migrate = self.fsoptionsDict["migratecb"].get_active() - if migrate: - fsystem = self.fsoptionsDict["migfstypeCombo"].get_active_value() - else: - migrate = 0 + if lv.format.type == "luks": + usedev = self.storage.devicetree.getChildren(lv)[0] + format = usedev.format + else: + usedev = lv + format = lv.format - if self.fsoptionsDict.has_key("resizecb") and self.fsoptionsDict["resizecb"].get_active(): - targetSize = self.fsoptionsDict["resizesb"].get_value_as_int() - else: - targetSize = None + if not lv.exists: + fmt_class = newfstypeCombo.get_active_value() + else: + # existing lv + fmt_class = self.fsoptionsDict["fstypeCombo"].get_active_value() - # set back if we are not formatting or migrating - origfstype = logrequest.origfstype - if not format and not migrate: - fsystem = origfstype + mountpoint = mountCombo.get_children()[0].get_text().strip() - mntpt = string.strip(mountCombo.get_children()[0].get_text()) + # validate logical volume name + lvname = lvnameEntry.get_text().strip() + if not lv.exists: + err = sanityCheckLogicalVolumeName(lvname) + if err: + self.intf.messageWindow(_("Illegal Logical Volume Name"), + err, custom_icon="error") + continue - if not logrequest or not logrequest.getPreExisting(): - # check size specification - badsize = 0 - try: - size = long(sizeEntry.get_text()) - except: - badsize = 1 + # check that the name is not already in use + used = 0 + for _lv in self.vg.lvs: + if lvname and lv.lvname == lvname: + continue - if badsize or size <= 0: - self.intf.messageWindow(_("Illegal size"), - _("The requested size as entered is " - "not a valid number greater " - "than 0."), custom_icon="error") - continue - else: - size = logrequest.size + if lv.lvname == lvname: + used = 1 + break - # is this an existing logical volume or one we're editting - if logrequest: - preexist = logrequest.getPreExisting() - else: - preexist = 0 + if used: + self.intf.messageWindow(_("Illegal logical volume name"), + _("The logical volume name \"%s\" is " + "already in use. Please pick " + "another.") % (lvname,), custom_icon="error") + continue # test mount point # check in pending logical volume requests # these may not have been put in master list of requests # yet if we have not hit 'OK' for the volume group creation - if fsystem.isMountable(): + if fmt_class().mountable and mountpoint: used = 0 - if logrequest: - curmntpt = logrequest.mountpoint - else: - curmntpt = None + curmntpt = format.mountpoint - for lv in self.logvolreqs: - if curmntpt and lv.mountpoint == curmntpt: + for _lv in self.logvolreqs: + if _lv.format.type == "luks": + # XXX this won't work if the lvs haven't been + # added to the tree yet + _usedev = self.tree.getChildren(_lv)[0] + _format = _usedev.format + else: + _usedev = _lv + _format = _lv.format + + if not _format.mountable or curmntpt and \ + _format.mountpoint == curmntpt: continue - if len(mntpt) == 0: - continue - if lv.mountpoint == mntpt: + if _format.mountpoint == mountpoint: used = 1 break @@ -580,47 +592,30 @@ class VolumeGroupEditor: self.intf.messageWindow(_("Mount point in use"), _("The mount point \"%s\" is in " "use. Please pick another.") % - (mntpt,), custom_icon="error") + (mountpoint,), + custom_icon="error") continue - # check out logical volumne name - lvname = string.strip(lvnameEntry.get_text()) + # check that size specification is numeric and positive + if not lv.exists: + badsize = 0 + try: + size = long(sizeEntry.get_text()) + except: + badsize = 1 - if not logrequest or not logrequest.getPreExisting(): - err = sanityCheckLogicalVolumeName(lvname) - if err: - self.intf.messageWindow(_("Illegal Logical Volume Name"),err, custom_icon="error") + if badsize or size <= 0: + self.intf.messageWindow(_("Illegal size"), + _("The requested size as entered is " + "not a valid number greater " + "than 0."), custom_icon="error") continue + else: + size = lv.size - # is it in use? - used = 0 - if logrequest: - origlvname = logrequest.logicalVolumeName - else: - origlvname = None - - for lv in self.logvolreqs: - if origlvname and lv.logicalVolumeName == origlvname: - continue - - if lv.logicalVolumeName == lvname: - used = 1 - break - - if used: - self.intf.messageWindow(_("Illegal logical volume name"), - _("The logical volume name \"%s\" is " - "already in use. Please pick " - "another.") % (lvname,), custom_icon="error") - continue - - # create potential request - request = copy.copy(logrequest) - request.encryption = copy.deepcopy(logrequest.encryption) - pesize = int(self.peCombo.get_active_value()) - size = lvm.clampLVSizeRequest(size, pesize, roundup=1) - - # do some final tests + # check that size specification is within limits + pesize = int(self.peCombo.get_active_value()) / 1024.0 + size = lvm.clampSize(size, pesize, roundup=True) maxlv = lvm.getMaxLVSize(pesize) if size > maxlv: self.intf.messageWindow(_("Not enough space"), @@ -635,107 +630,82 @@ class VolumeGroupEditor: custom_icon="error") continue - request.fstype = fsystem - - if request.fstype.isMountable(): - request.mountpoint = mntpt - else: - request.mountpoint = None - - request.preexist = preexist - request.logicalVolumeName = lvname - request.size = size - request.format = format - request.migrate = migrate - request.targetSize = targetSize - request.grow = 0 - - # this is needed to clear out any cached info about the device - # only a workaround - need to change way this is handled in - # partRequest.py really. - request.dev = None - - if self.lukscb and self.lukscb.get_active(): - if not request.encryption: - request.encryption = LUKSDevice(passphrase=self.partitions.encryptionPassphrase, format=1) + # Ok -- now we've done all the checks to validate the + # user-specified parameters. Time to set up the device... + if not lv.exists: + lv.name = lvname + lv.size = size + lv.req_grow = False + format = fmt_class(mountpoint=mountpoint) + if self.lukscb and self.lukscb.get_active() and \ + lv.format != "luks": + luksformat = format + format = getFormat("luks", + passphrase=self.storage.encryptionPassphrase) + luksdev = LUKSDevice("luks-%s" % lv.name, + format=luksformat, + parents=lv) + + actions.append(ActionCreateDevice(lv)) + if luksdev: + actions.append(ActionCreateDevice(luksdev)) else: - request.encryption = None - - # make list of original logvol requests so we can skip them - # in tests below. We check for mount point name conflicts - # above within the current volume group, so it is not - # necessary to do now. - err = request.sanityCheckRequest(self.partitions, skipMntPtExistCheck=1, pesize=pesize) - if err is None: - skiplist = [] - for lv in self.origvolreqs: - skiplist.append(lv.uniqueID) - - err = request.isMountPointInUse(self.partitions, requestSkipList=skiplist) + # existing lv + if self.fsoptionsDict.has_key("formatcb") and \ + self.fsoptionsDict["formatcb"].get_active(): + format = fmt_class(mountpoint=mountpoint) + if self.lukscb and self.lukscb.get_active() and \ + lv.format.type != "luks": + luksformat = format + format = getFormat("luks", + device=lv.path, + passphrase=self.storage.encryptionPassphrase) + luksdev = LUKSDevice("luks-%s" % lv.name, + format=luksformat, + parents=lv) + + if self.fsoptionsDict.has_key("migratecb") and \ + self.fsoptionsDict["migratecb"].get_active(): + migrate_class = self.fsoptionsDict["migfstypeCombo"].get_active_value() + + if self.fsoptionsDict.has_key("resizecb") and self.fsoptionsDict["resizecb"].get_active(): + targetSize = self.fsoptionsDict["resizesb"].get_value_as_int() + + if format: + actions.append(ActionDestroyFormat(usedev)) + actions.append(ActionCreateFormat(usedev, format) + if luksdev: + actions.append(ActionCreateDevice(luksdev)) + + err = self.storage.sanityCheckRequest(lv, + skipMntPtExistCheck=1, + pesize=pesize) + if not err: + err = self.storage.isMountPointInUse(lv) if err: self.intf.messageWindow(_("Error With Request"), "%s" % (err), custom_icon="error") continue - if (not request.format and - request.mountpoint and request.formatByDefault()): - if not queryNoFormatPreExisting(self.intf): - continue - - # see if there is room for request - (availSpaceMB, neededSpaceMB, fspace) = self.computeSpaceValues(usepe=pesize) - - tmplogreqs = [] - for l in self.logvolreqs: - if origlvname and l.logicalVolumeName == origlvname: + if usedev.format.exists and format.mountable and \ + self.storage.formatByDefault(usedev) and \ + not queryNoFormatPreExisting(self.intf): continue - - tmplogreqs.append(l) - - tmplogreqs.append(request) - neededSpaceMB = self.computeLVSpaceNeeded(tmplogreqs) - - if neededSpaceMB > availSpaceMB: - self.intf.messageWindow(_("Not enough space"), - _("The logical volumes you have " - "configured require %d MB, but the " - "volume group only has %d MB. Please " - "either make the volume group larger " - "or make the logical volume(s) smaller.") % (neededSpaceMB, availSpaceMB), custom_icon="error") - del tmplogreqs - continue # everything ok break - # now remove the previous request, insert request created above - if not isNew: - self.logvolreqs.remove(logrequest) - iter = self.getCurrentLogicalVolume() - self.logvolstore.remove(iter) - if request.targetSize is not None: - size = request.targetSize - # adjust the free space in the vg - if logrequest.targetSize is not None: - diff = request.targetSize - logrequest.targetSize - else: - diff = request.targetSize - request.size - self.origvgrequest.free -= diff - - self.logvolreqs.append(request) - - iter = self.logvolstore.append() - self.logvolstore.set_value(iter, 0, lvname) - if request.fstype and request.fstype.isMountable(): - self.logvolstore.set_value(iter, 1, mntpt) - else: - self.logvolstore.set_value(iter, 1, "N/A") - - self.logvolstore.set_value(iter, 2, "%Ld" % (size,)) + for action in actions: + oldaction = self.storage.devicetree.registerAction(action) + if oldaction: + self.actions.remove(oldaction) + self.actions.append(action) + self.updateLogVolStore() self.updateVGSpaceLabels() dialog.destroy() + return def editCurrentLogicalVolume(self): iter = self.getCurrentLogicalVolume() @@ -744,15 +714,11 @@ class VolumeGroupEditor: return logvolname = self.logvolstore.get_value(iter, 0) - logrequest = None - for lv in self.logvolreqs: - if lv.logicalVolumeName == logvolname: - logrequest = lv - - if logrequest is None: + lv = self.storage.devicetree.getDeviceByName(logvolname) + if lv is None: return - self.editLogicalVolume(logrequest) + self.editLogicalVolume(lv) def addLogicalVolumeCB(self, widget): if self.numAvailableLVSlots() < 1: @@ -761,8 +727,8 @@ class VolumeGroupEditor: "volumes per volume group.") % (lvm.MAX_LV_SLOTS,), custom_icon="error") return - (tspace, uspace, fspace) = self.computeSpaceValues() - if fspace <= 0: + (total, used, free) = self.computeSpaceValues() + if free <= 0: self.intf.messageWindow(_("No free space"), _("There is no room left in the " "volume group to create new logical " @@ -773,9 +739,10 @@ class VolumeGroupEditor: "logical volumes"), custom_icon="error") return - request = LogicalVolumeRequestSpec(fileSystemTypeGetDefault(), - size = fspace) - self.editLogicalVolume(request, isNew = 1) + lv = self.storage.newLV(self.vg, + fmt_type=self.storage.defaultFSType, + size=free) + self.editLogicalVolume(lv, isNew = 1) return def editLogicalVolumeCB(self, widget): @@ -798,9 +765,13 @@ class VolumeGroupEditor: if not rc: return - for lv in self.logvolreqs: - if lv.logicalVolumeName == logvolname: - self.logvolreqs.remove(lv) + name = "%s-%s" % (self.vg.name, logvolname) + lv = self.storage.devicetree.getDeviceByName(name) + action = ActionDestroyDevice(lv) + oldaction = self.storage.devicetree.registerAction(action) + if oldaction: + self.actions.remove(oldaction) + self.actions.append(action) self.logvolstore.remove(iter) @@ -820,9 +791,8 @@ class VolumeGroupEditor: partname = model.get_value(iter, 1) if val: - pvreq = self.partitions.getRequestByDeviceName(partname) - id = pvreq.uniqueID - pv.append(id) + dev = self.storage.devicetree.getDeviceByName(partname) + pv.append(dev) next = model.iter_next(iter) currow = currow + 1 @@ -831,10 +801,9 @@ class VolumeGroupEditor: def computeVGSize(self, pvlist, curpe): availSpaceMB = 0L - for id in pvlist: - pvreq = self.partitions.getRequestByID(id) - pvsize = pvreq.getActualSize(self.partitions, self.diskset) - pvsize = lvm.clampPVSize(pvsize, curpe) - (curpe/1024) + for pv in pvlist: + # XXX why the subtraction? + pvsize = lvm.clampSize(pvsize, curpe) - (curpe/1024) # have to clamp pvsize to multiple of PE availSpaceMB = availSpaceMB + pvsize @@ -845,29 +814,29 @@ class VolumeGroupEditor: def computeLVSpaceNeeded(self, logreqs): neededSpaceMB = 0 for lv in logreqs: - neededSpaceMB = neededSpaceMB + lv.getActualSize(self.partitions, self.diskset, True) + neededSpaceMB = neededSpaceMB + lv.size return neededSpaceMB def updateLogVolStore(self): self.logvolstore.clear() - for lv in self.logvolreqs: + for lv in self.vg.lvs: iter = self.logvolstore.append() - size = lv.getActualSize(self.partitions, self.diskset, True) - lvname = lv.logicalVolumeName - mntpt = lv.mountpoint - if lvname: + if lv.format.type == "luks": + usedev = self.storage.devicetree.getChildren(lv)[0] + else: + usedev = lv + + mntpt = getattr(usedev.format, "mountpoint", "") + if lv.lvname: self.logvolstore.set_value(iter, 0, lvname) - if lv.fstype and lv.fstype.isMountable(): - if mntpt: - self.logvolstore.set_value(iter, 1, mntpt) - else: - self.logvolstore.set_value(iter, 1, "") + if usedev.format.type and usedev.format.mountable: + self.logvolstore.set_value(iter, 1, mntpt) else: self.logvolstore.set_value(iter, 1, "N/A") - self.logvolstore.set_value(iter, 2, "%Ld" % (size,)) + self.logvolstore.set_value(iter, 2, "%Ld" % (lv.size,)) def updateVGSpaceLabels(self, alt_pvlist=None): if alt_pvlist == None: @@ -875,21 +844,21 @@ class VolumeGroupEditor: else: pvlist = alt_pvlist - (tspace, uspace, fspace) = self.computeSpaceValues(alt_pvlist=pvlist) + (total, used, free) = self.computeSpaceValues(alt_pvlist=pvlist) - self.totalSpaceLabel.set_text("%10.2f MB" % (tspace,)) - self.usedSpaceLabel.set_text("%10.2f MB" % (uspace,)) + self.totalSpaceLabel.set_text("%10.2f MB" % (total,)) + self.usedSpaceLabel.set_text("%10.2f MB" % (used,)) - if tspace > 0: - usedpercent = (100.0*uspace)/tspace + if total > 0: + usedpercent = (100.0*used)/total else: usedpercent = 0.0 self.usedPercentLabel.set_text("(%4.1f %%)" % (usedpercent,)) - self.freeSpaceLabel.set_text("%10.2f MB" % (fspace,)) - if tspace > 0: - freepercent = (100.0*fspace)/tspace + self.freeSpaceLabel.set_text("%10.2f MB" % (free,)) + if total > 0: + freepercent = (100.0*free)/total else: freepercent = 0.0 @@ -907,12 +876,16 @@ class VolumeGroupEditor: if rc == 2: self.destroy() + # cancel all the actions we have registered + self.actions.reverse() + for action in self.actions: + self.storage.devicetree.cancelAction(action) return None pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model()) - pesize = int(self.peCombo.get_active_value()) + pesize = int(self.peCombo.get_active_value()) / 1024.0 availSpaceMB = self.computeVGSize(pvlist, pesize) - neededSpaceMB = self.computeLVSpaceNeeded(self.logvolreqs) + neededSpaceMB = self.computeLVSpaceNeeded(self.vg.lvs) if neededSpaceMB > availSpaceMB: self.intf.messageWindow(_("Not enough space"), @@ -924,20 +897,21 @@ class VolumeGroupEditor: continue # check volume name - volname = string.strip(self.volnameEntry.get_text()) + volname = self.volnameEntry.get_text().strip() err = sanityCheckVolumeGroupName(volname) if err: self.intf.messageWindow(_("Invalid Volume Group Name"), err, custom_icon="error") continue - if self.origvgrequest: - origvname = self.origvgrequest.volumeGroupName + if self.vg: + origvname = self.vg.name else: origname = None if origvname != volname: - if self.partitions.isVolumeGroupNameInUse(volname): + # maybe we should see if _any_ device has this name + if volname in [vg.name for vg in self.storage.vgs]: self.intf.messageWindow(_("Name in use"), _("The volume group name \"%s\" is " "already in use. Please pick " @@ -946,39 +920,27 @@ class VolumeGroupEditor: continue # get physical extent - pesize = int(self.peCombo.get_active_value()) + pesize = int(self.peCombo.get_active_value()) / 1024.0 # everything ok break - request = VolumeGroupRequestSpec(physvols = pvlist, vgname = volname, - pesize = pesize) - - # if it was preexisting, it still should be - if self.origvgrequest and self.origvgrequest.getPreExisting(): - request.preexist = 1 - elif self.origvgrequest: - request.format = self.origvgrequest.format - - return (request, self.logvolreqs) + return self.actions def destroy(self): if self.dialog: self.dialog.destroy() self.dialog = None - def __init__(self, anaconda, partitions, diskset, intf, parent, origvgrequest, isNew = 0): - self.partitions = partitions - self.diskset = diskset - self.origvgrequest = origvgrequest + def __init__(self, anaconda, intf, parent, vg, isNew = 0): + self.storage = anaconda.id.storage + self.vg = vg self.isNew = isNew self.intf = intf self.parent = parent + self.actions = [] - self.availlvmparts = self.partitions.getAvailLVMPartitions(self.origvgrequest, - self.diskset) - self.logvolreqs = self.partitions.getLVMLVForVG(self.origvgrequest) - self.origvolreqs = copy.copy(self.logvolreqs) + self.availlvmparts = self.storage.unusedPVs(vg=vg) # if no PV exist, raise an error message and return if len(self.availlvmparts) < 1: @@ -997,8 +959,8 @@ class VolumeGroupEditor: tstr = _("Make LVM Volume Group") else: try: - tstr = _("Edit LVM Volume Group: %s") % (origvgrequest.volumeGroupName,) - except: + tstr = _("Edit LVM Volume Group: %s") % (vg.name,) + except AttributeError: tstr = _("Edit LVM Volume Group") dialog = gtk.Dialog(tstr, self.parent) @@ -1014,17 +976,17 @@ class VolumeGroupEditor: row = 0 # volume group name - if not origvgrequest.getPreExisting(): + if not vg.exists: lbl = createAlignedLabel(_("_Volume Group Name:")) self.volnameEntry = gtk.Entry(16) lbl.set_mnemonic_widget(self.volnameEntry) if not self.isNew: - self.volnameEntry.set_text(self.origvgrequest.volumeGroupName) + self.volnameEntry.set_text(self.vg.name) else: - self.volnameEntry.set_text(lvm.createSuggestedVGName(self.partitions, anaconda.id.network)) + self.volnameEntry.set_text(storage.suggestedVGName(anaconda.id.network)) else: lbl = createAlignedLabel(_("Volume Group Name:")) - self.volnameEntry = gtk.Label(self.origvgrequest.volumeGroupName) + self.volnameEntry = gtk.Label(self.vg.name) maintable.attach(lbl, 0, 1, row, row + 1, gtk.EXPAND|gtk.FILL, gtk.SHRINK) @@ -1032,9 +994,9 @@ class VolumeGroupEditor: row = row + 1 lbl = createAlignedLabel(_("_Physical Extent:")) - self.peCombo = self.createPEOptionMenu(self.origvgrequest.pesize) + self.peCombo = self.createPEOptionMenu(self.vg.peSize * 1024) lbl.set_mnemonic_widget(self.peCombo) - if origvgrequest.getPreExisting(): + if vg.exists: self.peCombo.set_sensitive(False) maintable.attach(lbl, 0, 1, row, row + 1, @@ -1042,8 +1004,8 @@ class VolumeGroupEditor: maintable.attach(self.peCombo, 1, 2, row, row + 1, gtk.EXPAND|gtk.FILL, gtk.SHRINK) row = row + 1 - (self.lvmlist, sw) = self.createAllowedLvmPartitionsList(self.availlvmparts, self.origvgrequest.physicalVolumes, self.partitions, origvgrequest.getPreExisting()) - if origvgrequest.getPreExisting(): + (self.lvmlist, sw) = self.createAllowedLvmPartitionsList(self.availlvmparts, self.storage.vgs) + if vg.exists: self.lvmlist.set_sensitive(False) self.lvmlist.set_size_request(275, 80) lbl = createAlignedLabel(_("Physical Volumes to _Use:")) @@ -1105,15 +1067,23 @@ class VolumeGroupEditor: gobject.TYPE_STRING, gobject.TYPE_STRING) - if self.logvolreqs: - for lvrequest in self.logvolreqs: + if self.vg.lvs: + for lv in self.vg.lvs: iter = self.logvolstore.append() - self.logvolstore.set_value(iter, 0, lvrequest.logicalVolumeName) - if lvrequest.mountpoint is not None: - self.logvolstore.set_value(iter, 1, lvrequest.mountpoint) + self.logvolstore.set_value(iter, 0, lv.lvname) + if lv.format.type == "luks": + usedev = self.storage.devicetree.getChildren(lv)[0] + format = usedev.format + else: + usedev = lv + format = usedev.format + + if getattr(format, "mountpoint", None): + self.logvolstore.set_value(iter, 1, + format.mountpoint) else: self.logvolstore.set_value(iter, 1, "") - self.logvolstore.set_value(iter, 2, "%Ld" % (lvrequest.getActualSize(self.partitions, self.diskset, True))) + self.logvolstore.set_value(iter, 2, "%Ld" % lv.size) self.logvollist = gtk.TreeView(self.logvolstore) col = gtk.TreeViewColumn(_("Logical Volume Name"), diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index 04e45db6a..272b8bd00 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -26,9 +26,7 @@ import gobject import gtk import gui -from fsset import * -from cryptodev import LUKSDevice -from partRequests import * +from storage.devices import PartitionDevice from partition_ui_helpers_gui import * from constants import * @@ -50,13 +48,6 @@ class PartitionEditor: adj.clamp_page(size, adj.upper) fillmaxszsb.set_adjustment(adj) - def cylspinchangedCB(self, widget, data): - (dev, startcylspin, endcylspin, bycyl_sizelabel) = data - startsec = dev.startCylinderToSector(startcylspin.get_value_as_int()) - endsec = dev.endCylinderToSector(endcylspin.get_value_as_int()) - cursize = (endsec - startsec)/2048 - bycyl_sizelabel.set_text("%s" % (int(cursize))) - def fillmaxszCB(self, widget, spin): spin.set_sensitive(widget.get_active()) @@ -111,150 +102,141 @@ class PartitionEditor: while 1: rc = self.dialog.run() + actions = [] # user hit cancel, do nothing if rc == 2: self.destroy() return None - if self.origrequest.type == REQUEST_NEW: + if not self.origrequest.exists: # read out UI into a partition specification - filesystem = self.newfstypeCombo.get_active_value() - - request = copy.copy(self.origrequest) - request.fstype = filesystem - request.format = True - - if request.fstype.isMountable(): - request.mountpoint = self.mountCombo.get_children()[0].get_text() - else: - request.mountpoint = None + fmt_class = self.newfstypeCombo.get_active_value() + # there's nothing about origrequest we care about + #request = copy.copy(self.origrequest) + mountpoint = self.mountCombo.get_children()[0].get_text() if self.primonlycheckbutton.get_active(): - primonly = True + primary = True else: - primonly = None + primary = None + format = fmt_class(mountpoint=mountpoint) if self.lukscb and self.lukscb.get_active(): - if not request.encryption: - request.encryption = LUKSDevice(passphrase = self.partitions.encryptionPassphrase, format=1) + luksformat = format + format = getFormat("luks", + passphrase=self.storage.encryptionPassphrase) + luksdev = LUKSDevice("luks%d" % self.storage.nextID, + format=luksformat, + parents=request) + + if self.fixedrb.get_active(): + grow = None else: - request.encryption = None - - if not self.newbycyl: - if self.fixedrb.get_active(): - grow = None - else: - grow = True - - self.sizespin.update() - - if self.fillmaxszrb.get_active(): - self.fillmaxszsb.update() - maxsize = self.fillmaxszsb.get_value_as_int() - else: - maxsize = None - - allowdrives = [] - model = self.driveview.get_model() - iter = model.get_iter_first() - while iter: - val = model.get_value(iter, 0) - drive = model.get_value(iter, 1) - - if val: - allowdrives.append(drive) - - iter = model.iter_next(iter) - - if len(allowdrives) == len(self.diskset.disks.keys()): - allowdrives = None - - request.size = self.sizespin.get_value_as_int() - request.drive = allowdrives - request.grow = grow - request.primary = primonly - request.maxSizeMB = maxsize - else: - self.startcylspin.update() - self.endcylspin.update() - - request.start = self.startcylspin.get_value_as_int() - request.end = self.endcylspin.get_value_as_int() - request.primary = primonly - - if request.end <= request.start: - self.intf.messageWindow(_("Error With Request"), - _("The end cylinder must be " - "greater than the start " - "cylinder."), custom_icon="error") + grow = True - continue + self.sizespin.update() - err = request.sanityCheckRequest(self.partitions) + if self.fillmaxszrb.get_active(): + self.fillmaxszsb.update() + maxsize = self.fillmaxszsb.get_value_as_int() + else: + maxsize = None + + allowdrives = [] + model = self.driveview.get_model() + iter = model.get_iter_first() + while iter: + val = model.get_value(iter, 0) + drive = model.get_value(iter, 1) + + if val: + allowdrives.append(drive) + + iter = model.iter_next(iter) + + if len(allowdrives) == len(self.storage.disks): + allowdrives = None + + size = self.sizespin.get_value_as_int() + disks = [] + if allowdrives: + for drive in allowdrives: + for disk in self.storage.disks: + if disk.name == drive: + disks.append(disk) + + # TODO: when will we set bootable? + request = PartitionDevice("new%d" % self.storage.nextID, + format=format, + size=size, + grow=grow, + maxsize=maxsize, + primary=primary, + parents=disks) + + err = storage.sanityCheckRequest(request) if not err: - err = doUIRAIDLVMChecks(request, self.diskset) + err = doUIRAIDLVMChecks(request, self.storage) if err: self.intf.messageWindow(_("Error With Request"), "%s" % (err), custom_icon="error") continue + + # we're all set, so create the actions + if luksdev: + actions.append(ActionCreateDevice(luksdev)) + actions.append(ActionCreateDevice(request)) else: # preexisting partition, just set mount point and format flag request = copy.copy(self.origrequest) - request.encryption = copy.deepcopy(self.origrequest.encryption) - - if self.fsoptionsDict.has_key("formatcb"): - request.format = self.fsoptionsDict["formatcb"].get_active() - if request.format: - request.fstype = self.fsoptionsDict["fstypeCombo"].get_active_value() - else: - request.format = 0 - - if self.fsoptionsDict.has_key("migratecb"): - request.migrate = self.fsoptionsDict["migratecb"].get_active() - if request.migrate: - request.fstype =self.fsoptionsDict["migfstypeCombo"].get_active_value() - else: - request.migrate = 0 - - if self.fsoptionsDict.has_key("resizecb") and self.fsoptionsDict["resizecb"].get_active(): - request.targetSize = self.fsoptionsDict["resizesb"].get_value_as_int() - else: - request.targetSize = None - - # set back if we are not formatting or migrating - origfstype = self.origrequest.origfstype - if not request.format and not request.migrate: - request.fstype = origfstype - - if request.fstype.isMountable(): - request.mountpoint = self.mountCombo.get_children()[0].get_text() - else: - request.mountpoint = None - - lukscb = self.fsoptionsDict.get("lukscb") - if lukscb and lukscb.get_active(): - if not request.encryption: - request.encryption = LUKSDevice(passphrase=self.partitions.encryptionPassphrase, format=1) - else: - request.encryption = None - - err = request.sanityCheckRequest(self.partitions) + mountpoint = self.mountCombo.get_children()[0].get_text() + if self.fsoptionsDict.has_key("formatcb") and \ + self.fsoptionsDict["formatcb"].get_active(): + fmt_class = self.fsoptionsDict["fstypeCombo"].get_active_value() + format = fmt_class(mountpoint=mountpoint) + if self.fsoptionsDict.has_key("lukscb") and \ + lukscb.get_active() and \ + request.format.type != "luks": + luksdev = LUKSDevice("luks%d" % self.storage.nextID, + format=format, + parents=request) + actions.append(ActionCreateDevice(luksdev) + format = getFormat("luks", + device=self.origrequest.path, + passphrase=self.storage.encryptionPassphrase) + actions.append(ActionCreateFormat(request, format)) + + if self.fsoptionsDict.has_key("migratecb") and \ + self.fsoptionsDict["migratecb"].get_active(): + format = getFormat(self.fsoptionsDict["migfstypeCombo"].get_active_value(), mountpoint=mountpoint) + # TODO: implement ActionMigrateFormat + #actions.append(ActionMigrateFormat(request, format)) + + if self.fsoptionsDict.has_key("resizecb") and \ + self.fsoptionsDict["resizecb"].get_active(): + size = self.fsoptionsDict["resizesb"].get_value_as_int() + actions.append(ActionResizeDevice(request, size) + if request.format.type != "none": + actions.append(ActionResizeFormat(request, size)) + + err = self.storage.sanityCheckRequest(request) if err: self.intf.messageWindow(_("Error With Request"), "%s" % (err), custom_icon="error") continue - if (not request.format and - request.mountpoint and request.formatByDefault()): + if request.format.exists and \ + getattr(request, "mountpoint", None) and \ + self.storage.formatByDefault(request)): if not queryNoFormatPreExisting(self.intf): continue - - # everything ok, fall out of loop + + # everything ok, fall out of loop break - - return request + + return actions def destroy(self): if self.dialog: @@ -265,8 +247,7 @@ class PartitionEditor: def __init__(self, anaconda, parent, origrequest, isNew = 0, restrictfs = None): self.anaconda = anaconda - self.partitions = self.anaconda.id.partitions - self.diskset = self.anaconda.id.diskset + self.storage = self.anaconda.id.storage self.intf = self.anaconda.intf self.origrequest = origrequest self.isNew = isNew @@ -275,10 +256,7 @@ class PartitionEditor: if isNew: tstr = _("Add Partition") else: - try: - tstr = _("Edit Partition: /dev/%s") % (origrequest.device,) - except: - tstr = _("Edit Partition") + tstr = _("Edit Partition: %s") % (origrequest.path,) self.dialog = gtk.Dialog(tstr, self.parent) gui.addFrame(self.dialog) @@ -291,24 +269,36 @@ class PartitionEditor: maintable.set_col_spacings(5) row = 0 - # see if we are creating a floating request or by cylinder - if self.origrequest.type == REQUEST_NEW: - self.newbycyl = self.origrequest.start != None + # if this is a luks device we need to grab info from two devices + # to make it seem like one device. wee! + if self.origrequest.format.type == "luks": + luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] + fmt_type = luksdev.format.type + mountpoint = getattr(luksdev.format, "mountpoint", None) + fslabel = getattr(luksdev.format, "label", None) + # XXX might need to add migrate stuff here, too + usereq = luksdev + else: + usereq = self.origrequest # Mount Point entry lbl = createAlignedLabel(_("_Mount Point:")) maintable.attach(lbl, 0, 1, row, row + 1) - self.mountCombo = createMountPointCombo(origrequest) + self.mountCombo = createMountPointCombo(usereq) lbl.set_mnemonic_widget(self.mountCombo) maintable.attach(self.mountCombo, 1, 2, row, row + 1) row = row + 1 # Partition Type - if self.origrequest.type == REQUEST_NEW: + if not self.origrequest.format.exists: lbl = createAlignedLabel(_("File System _Type:")) maintable.attach(lbl, 0, 1, row, row + 1) - self.newfstypeCombo = createFSTypeMenu(self.origrequest.fstype, + if luksdev: + usereq = luksdev + else: + usereq = self.origrequest + self.newfstypeCombo = createFSTypeMenu(usereq.format.type, fstypechangeCB, self.mountCombo, availablefstypes = restrictfs) @@ -320,103 +310,49 @@ class PartitionEditor: row = row + 1 # allowable drives - if self.origrequest.type == REQUEST_NEW: - if not self.newbycyl: - lbl = createAlignedLabel(_("Allowable _Drives:")) - maintable.attach(lbl, 0, 1, row, row + 1) - - self.driveview = createAllowedDrivesList(self.diskset.disks, - self.origrequest.drive, - selectDrives=False, - disallowDrives=[self.anaconda.updateSrc]) - lbl.set_mnemonic_widget(self.driveview) - sw = gtk.ScrolledWindow() - sw.add(self.driveview) - sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) - sw.set_shadow_type(gtk.SHADOW_IN) - maintable.attach(sw, 1, 2, row, row + 1) - self.driveview.set_size_request(375, 80) - else: - maintable.attach(createAlignedLabel(_("Drive:")), - 0, 1, row, row + 1) - maintable.attach(createAlignedLabel(origrequest.drive[0]), - 1, 2, row, row + 1) + if not self.origrequest.exists: + lbl = createAlignedLabel(_("Allowable _Drives:")) + maintable.attach(lbl, 0, 1, row, row + 1) + + self.driveview = createAllowedDrivesList(self.storage.disks, + self.origrequest.req_disks, + selectDrives=False, + disallowDrives=[self.anaconda.updateSrc]) + lbl.set_mnemonic_widget(self.driveview) + sw = gtk.ScrolledWindow() + sw.add(self.driveview) + sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + sw.set_shadow_type(gtk.SHADOW_IN) + maintable.attach(sw, 1, 2, row, row + 1) + self.driveview.set_size_request(375, 80) row = row + 1 # original fs label - if self.origrequest.type != REQUEST_NEW and self.origrequest.fslabel: + if usereq.format.exists and \ + getattr(usereq.format, "label", None): maintable.attach(createAlignedLabel(_("Original File System " "Label:")), 0, 1, row, row + 1) - fslabel = gtk.Label(self.origrequest.fslabel) + fslabel = gtk.Label(usereq.format.label) maintable.attach(fslabel, 1, 2, row, row + 1) - row = row + 1 # size - if self.origrequest.type == REQUEST_NEW: - if not self.newbycyl: - # Size specification - lbl = createAlignedLabel(_("_Size (MB):")) - maintable.attach(lbl, 0, 1, row, row + 1) - sizeAdj = gtk.Adjustment(value = 1, lower = 1, - upper = MAX_PART_SIZE, step_incr = 1) - self.sizespin = gtk.SpinButton(sizeAdj, digits = 0) - self.sizespin.set_property('numeric', True) - - if self.origrequest.size: - self.sizespin.set_value(self.origrequest.size) - - lbl.set_mnemonic_widget(self.sizespin) - maintable.attach(self.sizespin, 1, 2, row, row + 1) - bycyl_sizelabel = None - else: - # XXX need to add partition by size and - # wire in limits between start and end - dev = self.diskset.disks[origrequest.drive[0]].dev - maintable.attach(createAlignedLabel(_("Size (MB):")), - 0, 1, row, row + 1) - bycyl_sizelabel = createAlignedLabel("") - maintable.attach(bycyl_sizelabel, 1, 2, row, row + 1) - row = row + 1 + if not self.origrequest.exists: + # Size specification + lbl = createAlignedLabel(_("_Size (MB):")) + maintable.attach(lbl, 0, 1, row, row + 1) + sizeAdj = gtk.Adjustment(value = 1, lower = 1, + upper = MAX_PART_SIZE, step_incr = 1) + self.sizespin = gtk.SpinButton(sizeAdj, digits = 0) + self.sizespin.set_property('numeric', True) - lbl = createAlignedLabel(_("_Start Cylinder:")) - maintable.attach(lbl, 0, 1, row, row + 1) - - (maxcyl, h, s) = self.diskset.disks[origrequest.drive[0]].device.biosGeometry - cylAdj = gtk.Adjustment(value=origrequest.start, - lower=origrequest.start, - upper=maxcyl, - step_incr=1) - self.startcylspin = gtk.SpinButton(cylAdj, digits=0) - self.startcylspin.set_property('numeric', True) - lbl.set_mnemonic_widget(self.startcylspin) - maintable.attach(self.startcylspin, 1, 2, row, row + 1) - row = row + 1 - - endcylAdj = gtk.Adjustment(value=origrequest.end, - lower=origrequest.start, - upper=maxcyl, - step_incr=1) - lbl = createAlignedLabel(_("_End Cylinder:")) - maintable.attach(lbl, 0, 1, row, row + 1) - self.endcylspin = gtk.SpinButton(endcylAdj, digits = 0) - self.endcylspin.set_property('numeric', True) - lbl.set_mnemonic_widget(self.endcylspin) - maintable.attach(self.endcylspin, 1, 2, row, row + 1) - - self.startcylspin.connect("value-changed", self.cylspinchangedCB, - (dev, self.startcylspin, - self.endcylspin, bycyl_sizelabel)) - self.endcylspin.connect("value-changed", self.cylspinchangedCB, - (dev, self.startcylspin, - self.endcylspin, bycyl_sizelabel)) - - startsec = dev.startCylinderToSector(origrequest.start) - endsec = dev.endCylinderToSector(origrequest.end) - cursize = (endsec - startsec)/2048 - bycyl_sizelabel.set_text("%s" % (int(cursize))) + if self.origrequest.req_size: + self.sizespin.set_value(self.origrequest.req_size) + + lbl.set_mnemonic_widget(self.sizespin) + maintable.attach(self.sizespin, 1, 2, row, row + 1) else: self.sizespin = None @@ -425,45 +361,43 @@ class PartitionEditor: # format/migrate options for pre-existing partitions, as long as they # aren't protected (we'd still like to be able to mount them, though) self.fsoptionsDict = {} - if self.origrequest.type == REQUEST_PREEXIST and self.origrequest.fstype and not self.origrequest.getProtected(): - (row, self.fsoptionsDict) = createPreExistFSOptionSection(self.origrequest, maintable, row, self.mountCombo, self.partitions) + if self.origrequest.exists and \ + usereq.format.type != "none" and \ + not self.storage.isProtected(self.origrequest): + (row, self.fsoptionsDict) = createPreExistFSOptionSection(usereq, maintable, row, self.mountCombo, self.storage) # size options - if self.origrequest.type == REQUEST_NEW: - if not self.newbycyl: - (sizeframe, self.fixedrb, self.fillmaxszrb, - self.fillmaxszsb) = self.createSizeOptionsFrame(self.origrequest, - self.fillmaxszCB) - self.sizespin.connect("value-changed", self.sizespinchangedCB, - self.fillmaxszsb) - - maintable.attach(sizeframe, 0, 2, row, row + 1) - else: - # XXX need new by cyl options (if any) - pass + if not self.origrequest.exists: + (sizeframe, self.fixedrb, self.fillmaxszrb, + self.fillmaxszsb) = self.createSizeOptionsFrame(self.origrequest, + self.fillmaxszCB) + self.sizespin.connect("value-changed", self.sizespinchangedCB, + self.fillmaxszsb) + + maintable.attach(sizeframe, 0, 2, row, row + 1) row = row + 1 else: self.sizeoptiontable = None # create only as primary - if self.origrequest.type == REQUEST_NEW: + if not self.origrequest.exists: self.primonlycheckbutton = gtk.CheckButton(_("Force to be a _primary " "partition")) self.primonlycheckbutton.set_active(0) - if self.origrequest.primary: + if self.origrequest.req_primary: self.primonlycheckbutton.set_active(1) # only show if we have something other than primary - if not self.diskset.onlyPrimaryParts(): + if self.storage.extendedPartitionsSupported(): maintable.attach(self.primonlycheckbutton, 0, 2, row, row+1) row = row + 1 # checkbutton for encryption using dm-crypt/LUKS - if self.origrequest.type == REQUEST_NEW: + if not self.origrequest.exists: self.lukscb = gtk.CheckButton(_("_Encrypt")) self.lukscb.set_data("formatstate", 1) - if self.origrequest.encryption: + if self.origrequest.format.type == "luks": self.lukscb.set_active(1) else: self.lukscb.set_active(0) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index b753639ba..94d8781fa 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -99,7 +99,7 @@ class DiskStripeSlice: if event.button == 1: self.parent.selectSlice(self.partition, 1) elif event.type == gtk.gdk._2BUTTON_PRESS: - self.editCb() + self.editCB() return True @@ -183,12 +183,12 @@ class DiskStripeSlice: clip_width=xlength-1, clip_height=yheight-1) self.hideOrShowText() - def __init__(self, parent, partition, treeView, editCb): + def __init__(self, parent, partition, treeView, editCB): self.text = None self.partition = partition self.parent = parent self.treeView = treeView - self.editCb = editCb + self.editCB = editCB pgroup = parent.getGroup() self.group = pgroup.add(gnomecanvas.CanvasGroup) @@ -199,14 +199,14 @@ class DiskStripeSlice: self.update() class DiskStripe: - def __init__(self, drive, disk, group, tree, editCb): + def __init__(self, drive, disk, group, tree, editCB): self.disk = disk self.group = group self.tree = tree self.drive = drive self.slices = [] self.hash = {} - self.editCb = editCb + self.editCB = editCB self.selected = None # XXX hack but will work for now @@ -260,17 +260,17 @@ class DiskStripe: self.selected = None def add(self, partition): - stripe = DiskStripeSlice(self, partition, self.tree, self.editCb) + stripe = DiskStripeSlice(self, partition, self.tree, self.editCB) self.slices.append(stripe) self.hash[partition] = stripe class DiskStripeGraph: - def __init__(self, tree, editCb): + def __init__(self, tree, editCB): self.canvas = gnomecanvas.Canvas() self.diskStripes = [] self.textlabels = [] self.tree = tree - self.editCb = editCb + self.editCB = editCB self.next_ypos = 0.0 def __del__(self): @@ -335,7 +335,7 @@ class DiskStripeGraph: self.textlabels.append(text) group = self.canvas.root().add(gnomecanvas.CanvasGroup, x=0, y=yoff+textheight) - stripe = DiskStripe(drive, disk, group, self.tree, self.editCb) + stripe = DiskStripe(drive, disk, group, self.tree, self.editCB) self.diskStripes.append(stripe) self.next_ypos = self.next_ypos + STRIPE_HEIGHT+textheight+10 return stripe @@ -372,8 +372,6 @@ class DiskTreeModel(gtk.TreeStore): # (N_("Size (MB)"), gobject.TYPE_STRING, 1.0, 0, isLeaf), (N_("Format"), gobject.TYPE_OBJECT, 0.5, 0, isFormattable), (N_("Size (MB)"), gobject.TYPE_STRING, 1.0, 0, 0), - (N_("Start"), gobject.TYPE_STRING, 1.0, 0, 1), - (N_("End"), gobject.TYPE_STRING, 1.0, 0, 1), ("", gobject.TYPE_STRING, 0.0, 0, 0), # the following must be the last two ("IsLeaf", gobject.TYPE_BOOLEAN, 0.0, 1, 0), @@ -488,25 +486,7 @@ class DiskTreeModel(gtk.TreeStore): # not found the partition raise RuntimeError, "could not find partition" - """ returns partition 'id' of current selection in tree """ - def getCurrentPartition(self): - selection = self.view.get_selection() - model, iter = selection.get_selected() - if not iter: - return None - - pyobject = self.titleSlot['PyObject'] - try: - val = self.get_value(iter, pyobject) - if type(val) == type("/dev/"): - if val[:5] == '/dev/': - return None - - return val - except: - return None - - """ Return name of current selected drive (if a drive is highlighted) """ + """ Return the device representing the current selection """ def getCurrentDevice(self): selection = self.view.get_selection() model, iter = selection.get_selected() @@ -516,12 +496,10 @@ class DiskTreeModel(gtk.TreeStore): pyobject = self.titleSlot['PyObject'] try: val = self.get_value(iter, pyobject) - if type(val) == type("/dev/"): - if val[:5] == '/dev/': - return val - return None - except: - return None + except Exception: + val = None + + return val def resetSelection(self): pass @@ -632,8 +610,7 @@ class PartitionWindow(InstallWindow): return rc def getNext(self): - (errors, warnings) = self.partitions.sanityCheckAllRequests(self.diskset) - + (errors, warnings) = self.storage.sanityCheckAllRequests() if errors: labelstr1 = _("The partitioning scheme you requested " "caused the following critical errors.") @@ -649,6 +626,7 @@ class PartitionWindow(InstallWindow): raise gui.StayOnScreen if warnings: + # "storage configuration" labelstr1 = _("The partitioning scheme you requested " "generated the following warnings.") labelstr2 = _("Would you like to continue with " @@ -663,8 +641,7 @@ class PartitionWindow(InstallWindow): if rc != 1: raise gui.StayOnScreen - formatWarnings = getPreExistFormatWarnings(self.partitions, - self.diskset) + formatWarnings = getPreExistFormatWarnings(self.storage) if formatWarnings: labelstr1 = _("The following pre-existing partitions have been " "selected to be formatted, destroying all data.") @@ -676,7 +653,7 @@ class PartitionWindow(InstallWindow): commentstr = "" for (dev, type, mntpt) in formatWarnings: commentstr = commentstr + \ - "/dev/%s %s %s\n" % (dev,type,mntpt) + "%s %s %s\n" % (dev,type,mntpt) rc = self.presentPartitioningComments(_("Format Warnings"), labelstr1, labelstr2, @@ -695,161 +672,164 @@ class PartitionWindow(InstallWindow): def getPrev(self): self.diskStripeGraph.shutDown() - self.diskset.refreshDevices() - self.partitions.setFromDisk(self.diskset) + self.storage.reset() self.tree.clear() del self.parent return None - def getShortFSTypeName(self, name): - if name == "physical volume (LVM)": - return "LVM PV" - - return name - def populate(self, initial = 0): - - drives = self.diskset.disks.keys() - drives.sort() - self.tree.resetSelection() self.tree.clearHiddenPartitionsList() # first do LVM - lvmrequests = self.partitions.getLVMRequests() - if lvmrequests: + vgs = self.storage.vgs + if vgs: lvmparent = self.tree.append(None) self.tree[lvmparent]['Device'] = _("LVM Volume Groups") - for vgname in lvmrequests.keys(): - vgrequest = self.partitions.getRequestByVolumeGroupName(vgname) - rsize = vgrequest.getActualSize(self.partitions, self.diskset) + for vg in vgs: + rsize = vg.size vgparent = self.tree.append(lvmparent) - self.tree[vgparent]['Device'] = "%s" % (vgname,) - if vgrequest and vgrequest.type != REQUEST_NEW and vgrequest.fslabel: - self.tree[vgparent]['Label'] = "%s" % (vgrequest.fslabel,) - else: - self.tree[vgparent]['Label'] = "" + self.tree[vgparent]['Device'] = "%s" % vg.name + self.tree[vgparent]['Label'] = "" self.tree[vgparent]['Mount Point'] = "" - self.tree[vgparent]['Start'] = "" - self.tree[vgparent]['End'] = "" self.tree[vgparent]['Size (MB)'] = "%Ld" % (rsize,) - self.tree[vgparent]['PyObject'] = str(vgrequest.uniqueID) - for lvrequest in lvmrequests[vgname]: + self.tree[vgparent]['PyObject'] = vg + for lv in vg.lvs: + if lv.format.type == "luks": + # we'll want to grab format info from the mapped + # device, not the encrypted one + dm_dev = self.storage.devicetree.getChildren(lv)[0] + format = dm_dev.format + else: + format = lv.format iter = self.tree.append(vgparent) - self.tree[iter]['Device'] = lvrequest.logicalVolumeName - if lvrequest.fstype and lvrequest.mountpoint: - self.tree[iter]['Mount Point'] = lvrequest.mountpoint + self.tree[iter]['Device'] = lv.lvname + if format.mountable and format.mountpoint: + self.tree[iter]['Mount Point'] = format.mountpoint else: self.tree[iter]['Mount Point'] = "" - self.tree[iter]['Size (MB)'] = "%Ld" % (lvrequest.getActualSize(self.partitions, self.diskset, True),) - self.tree[iter]['PyObject'] = str(lvrequest.uniqueID) + self.tree[iter]['Size (MB)'] = "%Ld" % lv.size + self.tree[iter]['PyObject'] = vg - ptype = lvrequest.fstype.getName() - if lvrequest.isEncrypted(self.partitions, True) and lvrequest.format: + if lv.format.type == "luks" and not lv.format.exists: + # we're creating the LUKS header self.tree[iter]['Format'] = self.lock_pixbuf - elif lvrequest.format: + elif not format.exists: + # we're creating a format on the device self.tree[iter]['Format'] = self.checkmark_pixbuf - self.tree[iter]['IsFormattable'] = lvrequest.fstype.isFormattable() + self.tree[iter]['IsFormattable'] = format.formattable self.tree[iter]['IsLeaf'] = True - self.tree[iter]['Type'] = ptype - self.tree[iter]['Start'] = "" - self.tree[iter]['End'] = "" + self.tree[iter]['Type'] = lv.format.name + #self.tree[iter]['Start'] = "" + #self.tree[iter]['End'] = "" # handle RAID next - raidrequests = self.partitions.getRaidRequests() - if raidrequests: + mdarrays = self.storage.mdarrays + if mdarrays: raidparent = self.tree.append(None) self.tree[raidparent]['Device'] = _("RAID Devices") - for request in raidrequests: + for array in mdarrays: mntpt = None - if request and request.fstype and request.fstype.getName() == "physical volume (LVM)": - vgreq = self.partitions.getLVMVolumeGroupMemberParent(request) - if vgreq and vgreq.volumeGroupName: - if self.show_uneditable: - mntpt = vgreq.volumeGroupName - else: - self.tree.appendToHiddenPartitionsList(str(request.uniqueID)) - continue + if array.format.type == "luks": + # look up the mapped/decrypted device since that's + # where we'll find the format we want to display + dm_dev = self.storage.getChildren(array)[0] + format = dm_dev.format + else: + format = array.format + + if format.type == "lvmpv": + vg = None + for _vg in self.storage.vgs: + if _vg.dependsOn(array): + vg = _vg + break + if vg and self.show_uneditable: + mntpt = vg.name + elif vg: + self.tree.appendToHiddenPartitionsList(array.path) + continue else: mntpt = "" + elif format.mountable and format.mountpoint: + mntpt = format.mountpoint iter = self.tree.append(raidparent) if mntpt: self.tree[iter]["Mount Point"] = mntpt + else: + self.tree[iter]["Mount Point"] = "" - if request and request.mountpoint: - self.tree[iter]["Mount Point"] = request.mountpoint - - if request.fstype: - ptype = self.getShortFSTypeName(request.fstype.getName()) - - if request.isEncrypted(self.partitions, True) and request.format: + if format.type: + ptype = format.name + if array.format.type == "luks" and \ + not array.format.exists: self.tree[iter]['Format'] = self.lock_pixbuf - elif request.format: + elif not format.exists: self.tree[iter]['Format'] = self.checkmark_pixbuf - self.tree[iter]['IsFormattable'] = request.fstype.isFormattable() + self.tree[iter]['IsFormattable'] = format.formattable else: ptype = _("None") self.tree[iter]['IsFormattable'] = False - try: - device = "/dev/md%d" % (request.raidminor,) - except: + if array.minor is not None: + device = "%s" % array.path + else: device = "Auto" self.tree[iter]['IsLeaf'] = True self.tree[iter]['Device'] = device - if request and request.type != REQUEST_NEW and request.fslabel: - self.tree[iter]['Label'] = "%s" % (request.fslabel,) + if array.format.exists and getattr(format, "label", None): + self.tree[iter]['Label'] = "%s" % format.label else: self.tree[iter]['Label'] = "" self.tree[iter]['Type'] = ptype - self.tree[iter]['Start'] = "" - self.tree[iter]['End'] = "" - self.tree[iter]['Size (MB)'] = "%Ld" % (request.getActualSize(self.partitions, self.diskset),) - self.tree[iter]['PyObject'] = str(request.uniqueID) + self.tree[iter]['Size (MB)'] = "%Ld" % array.size + self.tree[iter]['PyObject'] = array # now normal partitions + disks = self.storage.disks drvparent = self.tree.append(None) self.tree[drvparent]['Device'] = _("Hard Drives") - for drive in drives: - disk = self.diskset.disks[drive] - + for disk in disks: # add a disk stripe to the graph - stripe = self.diskStripeGraph.add(drive, disk) + stripe = self.diskStripeGraph.add(disk.name, disk.partedDisk) # add a parent node to the tree parent = self.tree.append(drvparent) - self.tree[parent]['Device'] = '/dev/%s' % (drive,) - self.tree[parent]['PyObject'] = str('/dev/%s' % (drive,)) - (cylinders, heads, sectors) = disk.device.biosGeometry + self.tree[parent]['Device'] = "%s" % disk.path + self.tree[parent]['PyObject'] = disk + (cylinders, heads, sectors) = disk.partedDisk.device.biosGeometry sectorsPerCyl = heads * sectors - extendedParent = None - part = disk.getFirstPartition() + part = disk.partedDisk.getFirstPartition() while part: if part.type & parted.PARTITION_METADATA: part = part.nextPartition() continue + + extendedParent = None + partName = part.getDeviceNodeName() + device = self.storage.devicetree.getDeviceByName(partName) + if not device: + raise RuntimeError("can't find partition %s in device" + " tree" % partName) + # ignore the tiny < 1 MB partitions (#119479) if part.getSize(unit="MB") <= 1.0: - if not part.active or not part.getFlag(parted.PARTITION_BOOT): - part = part.nextPartition() + if not part.active or not device.bootable: continue stripe.add(part) - device = part.getDeviceNodeName() - request = self.partitions.getRequestByDeviceName(device) - - if part.type == parted.PARTITION_EXTENDED: + if device.isExtended: if extendedParent: raise RuntimeError, ("can't handle more than " "one extended partition per disk") extendedParent = self.tree.append(parent) iter = extendedParent - elif part.type & parted.PARTITION_LOGICAL: + elif device.isLogical: if not extendedParent: raise RuntimeError, ("crossed logical partition " "before extended") @@ -859,16 +839,28 @@ class PartitionWindow(InstallWindow): iter = self.tree.append(parent) self.tree[iter]['IsLeaf'] = True - if request and request.mountpoint: - self.tree[iter]['Mount Point'] = request.mountpoint + if device.format.type == "luks": + # look up the mapped/decrypted device in the tree + # the format we care about will be on it + dm_dev = self.storage.devicetree.getChildren(device)[0] + format = dm_dev.format + else: + format = device.format + + if format.mountable and format.mountpoint: + self.tree[iter]['Mount Point'] = format.mountpoint else: self.tree[iter]['Mount Point'] = "" - if request and request.fstype and request.fstype.getName() == "physical volume (LVM)": - vgreq = self.partitions.getLVMVolumeGroupMemberParent(request) - if vgreq and vgreq.volumeGroupName: + if format.type == "lvmpv": + vg = None + for _vg in self.storage.vgs: + if _vg.dependsOn(part): + vg = _vg + break + if vg and vg.name: if self.show_uneditable: - self.tree[iter]['Mount Point'] = vgreq.volumeGroupName + self.tree[iter]['Mount Point'] = vg.name else: self.tree.appendToHiddenPartitionsList(part) self.tree.remove(iter) @@ -876,26 +868,31 @@ class PartitionWindow(InstallWindow): else: self.tree[iter]['Mount Point'] = "" - if request.isEncrypted(self.partitions, True) and request.format: + if device.format.type == "luks" and \ + not device.format.exists: self.tree[iter]['Format'] = self.lock_pixbuf - elif request.format: + elif format.exists: self.tree[iter]['Format'] = self.checkmark_pixbuf - if request and request.fstype: - self.tree[iter]['IsFormattable'] = request.fstype.isFormattable() + if format.type: + self.tree[iter]['IsFormattable'] = device.format.formattable - if part.type & parted.PARTITION_FREESPACE: - ptype = _("Free space") - elif part.type == parted.PARTITION_EXTENDED: + if device.isExtended: ptype = _("Extended") - elif part.getFlag(parted.PARTITION_RAID) == 1: + elif format.type == "mdmember": ptype = _("software RAID") - parreq = self.partitions.getRaidMemberParent(request) - if parreq: + mds = self.storage.mdarrays + array = None + for _array in mds: + if _array.dependsOn(device): + array = _array + break + if array: if self.show_uneditable: - try: - mddevice = "/dev/md%d" % (parreq.raidminor,) - except: + if array.minor is not None: + mddevice = "%s" % array.path + else: + mddevice = "Auto" mddevice = "Auto" self.tree[iter]['Mount Point'] = mddevice else: @@ -906,48 +903,33 @@ class PartitionWindow(InstallWindow): else: self.tree[iter]['Mount Point'] = "" - if request and request.isEncrypted(self.partitions, True) and request.format: - self.tree[iter]['Format'] = self.lock_pixbuf - elif part.fileSystem: - if request and request.fstype != None: - ptype = self.getShortFSTypeName(request.fstype.getName()) - if ptype == "foreign": - ptype = map_foreign_to_fsname(part) - else: - ptype = part.fileSystem.type - - if request and request.isEncrypted(self.partitions, True) and request.format: + if device.format.type == "luks" and \ + not device.format.exists: self.tree[iter]['Format'] = self.lock_pixbuf - elif request and request.format: - self.tree[iter]['Format'] = self.checkmark_pixbuf else: - if request and request.fstype != None: - ptype = self.getShortFSTypeName(request.fstype.getName()) - - if ptype == "foreign": - ptype = map_foreign_to_fsname(part) + if format.type: + ptype = format.name else: ptype = _("None") if part.type & parted.PARTITION_FREESPACE: devname = _("Free") else: - devname = '/dev/%s' % (device,) + devname = "%s" % device.path self.tree[iter]['Device'] = devname - if request and request.type != REQUEST_NEW and request.fslabel: - self.tree[iter]['Label'] = "%s" % (request.fslabel,) + if format.exists and + getattr(format, "label", None): + self.tree[iter]['Label'] = "%s" % format.label else: self.tree[iter]['Label'] = "" self.tree[iter]['Type'] = ptype - self.tree[iter]['Start'] = str(disk.device.startSectorToCylinder(part.geometry.start)) - self.tree[iter]['End'] = str(disk.device.endSectorToCylinder(part.geometry.end)) size = part.getSize(unit="MB") if size < 1.0: sizestr = "< 1" else: sizestr = "%Ld" % (size) self.tree[iter]['Size (MB)'] = sizestr - self.tree[iter]['PyObject'] = part + self.tree[iter]['PyObject'] = device part = part.nextPartition() @@ -955,44 +937,52 @@ class PartitionWindow(InstallWindow): apply(canvas.set_scroll_region, canvas.root().get_bounds()) self.treeView.expand_all() - def treeActivateCb(self, view, path, col): + def treeActivateCB(self, view, path, col): if self.tree.getCurrentPartition(): - self.editCb() + self.editCB() - def treeSelectCb(self, selection, *args): + def treeSelectCB(self, selection, *args): model, iter = selection.get_selected() if not iter: return - partition = model[iter]['PyObject'] - if partition: + device = model[iter]['PyObject'] + if device: + # PyObject is always a Device but not always a PartitionDevice + try: + partition = device.partedPartition + except AttributeError: + return + self.diskStripeGraph.selectSlice(partition) def newCB(self, widget): - request = NewPartitionSpec(fileSystemTypeGetDefault(), size = 200) - - self.editPartitionRequest(request, isNew = 1) - - def deleteCb(self, widget): - curselection = self.tree.getCurrentPartition() - - if curselection: - if doDeletePartitionByRequest(self.intf, self.partitions, curselection): + device = self.storage.newPartition(fmt_type=self.storage.defaultFSType, + size=200) + self.editPartition(device, isNew=1) + + def deleteCB(self, widget): + """ Right now we can say that if the device is partitioned we + want to delete all of the devices it contains. At some point + we will want to support creation and removal of partitionable + devices. This will need some work when that time comes. + """ + device = self.tree.getCurrentDevice() + if getattr(device.partedDisk): + if doDeleteDependentDevices(self.intf, + self.storage, + device): + self.refresh() + elif doDeleteDevice(self.intf, + self.storage, + device): self.refresh() - else: - curdevice = self.tree.getCurrentDevice() - if curdevice and len(curdevice) > 5: - if doDeletePartitionsByDevice(self.intf, self.partitions, self.diskset, curdevice[5:]): - self.refresh() - else: - return - def resetCb(self, *args): + def resetCB(self, *args): if not confirmResetPartitionState(self.intf): return self.diskStripeGraph.shutDown() - self.diskset.refreshDevices() - self.partitions.setFromDisk(self.diskset) + self.storage.reset() self.tree.clear() self.populate() @@ -1000,25 +990,10 @@ class PartitionWindow(InstallWindow): self.diskStripeGraph.shutDown() self.tree.clear() - # XXXX - Backup some info which doPartitioning munges if it fails - origInfoDict = {} - for request in self.partitions.requests: - try: - origInfoDict[request.uniqueID] = (request.requestSize, request.currentDrive) - except: - pass - try: - autopart.doPartitioning(self.diskset, self.partitions) + autopart.doPartitioning(self.storage) rc = 0 except PartitioningError, msg: - try: - for request in self.partitions.requests: - if request.uniqueID in origInfoDict.keys(): - (request.requestSize, request.currentDrive) = origInfoDict[request.uniqueID] - except: - log.error("Failed to restore original info") - self.intf.messageWindow(_("Error Partitioning"), _("Could not allocate requested partitions: %s.") % (msg), custom_icon="error") @@ -1045,59 +1020,66 @@ class PartitionWindow(InstallWindow): rc = -1 else: rc = 0 - reqs = self.partitions.getBootableRequest() - if reqs: - for req in reqs: - req.ignoreBootConstraints = 1 + all_devices = self.storage.devicetree.devices.values() + bootDevs = [d for d in all_devices if d.bootable] + #if reqs: + # for req in reqs: + # req.ignoreBootConstraints = 1 if not rc == -1: self.populate() return rc - def editCb(self, *args): - part = self.tree.getCurrentPartition() - - (type, request) = doEditPartitionByRequest(self.intf, self.partitions, - part) - if request: - if type == "RAID": - self.editRaidRequest(request) - elif type == "LVMVG": - self.editLVMVolumeGroup(request) - elif type == "LVMLV": - vgrequest = self.partitions.getRequestByID(request.volumeGroup) - self.editLVMVolumeGroup(vgrequest) - elif type == "NEW": - self.editPartitionRequest(request, isNew = 1) - else: - self.editPartitionRequest(request) + def editCB(self, *args): + device = self.tree.getCurrentDevice() + if not device: + intf.messageWindow(_("Unable To Edit"), + _("You must select a device to edit"), + custom_icon="error") + return + + reason = self.storage.deviceImmutable(device) + if reason: + self.intf.messageWindow(_("Unable To Edit"), + _("You cannot edit this device:\n\n") + reason, + custom_icon="error") + return + + if device.type == "mdarray": + self.editRaidArray(device) + elif device.type == "lvmvg": + self.editLVMVolumeGroup(device) + elif device.type == "lvmlv": + self.editLVMVolumeGroup(device) + elif device.type = "partition": + self.editPartition(device) # isNew implies that this request has never been successfully used before - def editRaidRequest(self, raidrequest, isNew = 0): - raideditor = raid_dialog_gui.RaidEditor(self.partitions, - self.diskset, self.intf, - self.parent, raidrequest, - isNew) - origpartitions = self.partitions.copy() + def editRaidArray(self, raiddev, isNew = 0): + raideditor = raid_dialog_gui.RaidEditor(self.storage, + self.intf, + self.parent, + raiddev, + isNew) + # is a shallow copy enough? + #origpartitions = self.storage.devicetree.copy() while 1: - request = raideditor.run() + actions = raideditor.run() - if request is None: - return - - if not isNew: - self.partitions.removeRequest(raidrequest) - if raidrequest.getPreExisting(): - delete = partRequests.DeleteRAIDSpec(raidrequest.raidminor) - self.partitions.addDelete(delete) + if not actions: + break - self.partitions.addRequest(request) + for action in actions: + # FIXME: this needs to handle exceptions + self.storage.devicetree.registerAction(action) if self.refresh(): - if not isNew: - self.partitions = origpartitions.copy() + actions.reverse() + for action in actions: + self.storage.devicetree.cancelAction(action) if self.refresh(): raise RuntimeError, ("Returning partitions to state " "prior to RAID edit failed") @@ -1108,29 +1090,30 @@ class PartitionWindow(InstallWindow): raideditor.destroy() - def editPartitionRequest(self, origrequest, isNew = 0, restrictfs = None): + def editPartition(self, device, isNew = 0, restrictfs = None): parteditor = partition_dialog_gui.PartitionEditor(self.anaconda, self.parent, - origrequest, + device, isNew = isNew, restrictfs = restrictfs) while 1: - request = parteditor.run() + actions = parteditor.run() - if request is None: - return 0 + if not actions: + break - if not isNew: - self.partitions.removeRequest(origrequest) + for action in actions: + # XXX we should handle exceptions here + self.anaconda.id.storage.devicetree.registerAction(action) - self.partitions.addRequest(request) if self.refresh(): - # the add failed; remove what we just added and put - # back what was there if we removed it - self.partitions.removeRequest(request) - if not isNew: - self.partitions.addRequest(origrequest) + # autopart failed -- cancel the actions and try to get + # back to previous state + actions.reverse() + for action in actions: + self.anaconda.id.storage.devicetree.cancelAction(action) + if self.refresh(): # this worked before and doesn't now... raise RuntimeError, ("Returning partitions to state " @@ -1141,66 +1124,34 @@ class PartitionWindow(InstallWindow): parteditor.destroy() return 1 - def editLVMVolumeGroup(self, origvgrequest, isNew = 0): - vgeditor = lvm_dialog_gui.VolumeGroupEditor(self.anaconda, - self.partitions, - self.diskset, - self.intf, self.parent, - origvgrequest, isNew) + def editLVMVolumeGroup(self, device, isNew = 0): + # we don't really need to pass in self.storage if we're passing + # self.anaconda already + vgeditor = lvm_dialog_gui.VolumeGroupEditor(self.anaconda, + self.storage + self.intf, + self.parent, + device, + isNew) - origpartitions = self.partitions.copy() - origvolreqs = origpartitions.getLVMLVForVG(origvgrequest) - - while (1): - rc = vgeditor.run() - - # - # return code is either None or a tuple containing - # volume group request and logical volume requests - # - if rc is None: - return - - (vgrequest, logvolreqs) = rc - - # first add the volume group - if not isNew: - # if an lv was preexisting and isn't in the new lv requests, - # we need to add a delete for it. - for lv in origvolreqs: - if not lv.getPreExisting(): - continue - found = 0 - for newlv in logvolreqs: - if (newlv.getPreExisting() and - newlv.logicalVolumeName == lv.logicalVolumeName): - found = 1 - break - if found == 0: - delete = partRequests.DeleteLogicalVolumeSpec(lv.logicalVolumeName, - origvgrequest.volumeGroupName) - self.partitions.addDelete(delete) - - for lv in origvolreqs: - self.partitions.removeRequest(lv) + while True: + actions = vgeditor.run() - self.partitions.removeRequest(origvgrequest) + if not actions: + break - vgID = self.partitions.addRequest(vgrequest) - - # now add the logical volumes - for lv in logvolreqs: - lv.volumeGroup = vgID - if not lv.getPreExisting(): - lv.format = 1 - self.partitions.addRequest(lv) + for action in actions: + # FIXME: handle exceptions + self.storage.devicetree.registerAction(action) if self.refresh(): - if not isNew: - self.partitions = origpartitions.copy() - if self.refresh(): - raise RuntimeError, ("Returning partitions to state " - "prior to edit failed") + actions.reverse() + for action in actions: + self.storage.devicetree.cancelAction(action) + + if self.refresh(): + raise RuntimeError, ("Returning partitions to state " + "prior to edit failed") continue else: break @@ -1208,10 +1159,8 @@ class PartitionWindow(InstallWindow): vgeditor.destroy() - def makeLvmCB(self, widget): - if (not fileSystemTypeGet('physical volume (LVM)').isSupported() or - not lvm.has_lvm()): + if not getFormat("lvmpv").supported or not lvm.has_lvm(): self.intf.messageWindow(_("Not supported"), _("LVM is NOT supported on " "this platform."), type="ok", @@ -1219,20 +1168,20 @@ class PartitionWindow(InstallWindow): return request = VolumeGroupRequestSpec(format=True) - self.editLVMVolumeGroup(request, isNew = 1) + vg = self.storage.newVG() + self.editLVMVolumeGroup(vg, isNew = 1) return def makeraidCB(self, widget): - - if not fileSystemTypeGet('software RAID').isSupported(): + if not getFormat("software RAID").supported: self.intf.messageWindow(_("Not supported"), _("Software RAID is NOT supported on " "this platform."), type="ok", custom_icon="error") return - availminors = self.partitions.getAvailableRaidMinors() + availminors = self.storage.unusedMDMinors if len(availminors) < 1: self.intf.messageWindow(_("No RAID minor device numbers available"), _("A software RAID device cannot " @@ -1245,9 +1194,7 @@ class PartitionWindow(InstallWindow): # see if we have enough free software RAID partitions first # if no raid partitions exist, raise an error message and return - request = RaidRequestSpec(fileSystemTypeGetDefault()) - availraidparts = self.partitions.getAvailRaidPartitions(request, - self.diskset) + availraidparts = self.partitions.unusedMDMembers() dialog = gtk.Dialog(_("RAID Options"), self.parent) gui.addFrame(dialog) @@ -1300,7 +1247,7 @@ class PartitionWindow(InstallWindow): createRAIDpart.set_active(1) doRAIDclone.set_sensitive(0) createRAIDdev.set_sensitive(0) - if len(availraidparts) > 0 and len(self.diskset.disks.keys()) > 1: + if len(availraidparts) > 0 and len(self.storage.disks) > 1: doRAIDclone.set_sensitive(1) if len(availraidparts) > 1: @@ -1322,13 +1269,16 @@ class PartitionWindow(InstallWindow): # see which option they choose if createRAIDpart.get_active(): - rdrequest = NewPartitionSpec(fileSystemTypeGet("software RAID"), size = 200) - rc = self.editPartitionRequest(rdrequest, isNew = 1, restrictfs=["software RAID"]) + member = self.storage.newPartition(fmt_type="software RAID", + size=200) + rc = self.editPartitionRequest(member, + isNew = 1, + restrictfs=["software RAID"]) elif createRAIDdev.get_active(): - self.editRaidRequest(request, isNew=1) + array = self.storage.newMDArray(fmt_type=self.storage.defaultFSType) + self.editRaidArray(array, isNew=1) else: - cloneDialog = raid_dialog_gui.RaidCloneDialog(self.partitions, - self.diskset, + cloneDialog = raid_dialog_gui.RaidCloneDialog(self.storage self.intf, self.parent) if cloneDialog is None: @@ -1338,16 +1288,12 @@ class PartitionWindow(InstallWindow): custom_icon="error") return - while 1: - rc = cloneDialog.run() + if cloneDialog.run(): + self.refresh() - if rc: - self.refresh() - - cloneDialog.destroy() - return + cloneDialog.destroy() + return - def viewButtonCB(self, widget): self.show_uneditable = not widget.get_active() self.diskStripeGraph.shutDown() @@ -1356,13 +1302,9 @@ class PartitionWindow(InstallWindow): def getScreen(self, anaconda): self.anaconda = anaconda - self.fsset = anaconda.id.fsset - self.diskset = anaconda.id.diskset + self.storage = anaconda.id.storage self.intf = anaconda.intf - self.diskset.openDevices() - self.partitions = anaconda.id.partitions - self.show_uneditable = 1 checkForSwapNoMatch(anaconda) @@ -1376,9 +1318,9 @@ class PartitionWindow(InstallWindow): buttonBox.set_layout(gtk.BUTTONBOX_SPREAD) ops = ((_("Ne_w"), self.newCB), - (_("_Edit"), self.editCb), - (_("_Delete"), self.deleteCb), - (_("Re_set"), self.resetCb), + (_("_Edit"), self.editCB), + (_("_Delete"), self.deleteCB), + (_("Re_set"), self.resetCB), (_("R_AID"), self.makeraidCB), (_("_LVM"), self.makeLvmCB)) @@ -1389,12 +1331,12 @@ class PartitionWindow(InstallWindow): self.tree = DiskTreeModel() self.treeView = self.tree.getTreeView() - self.treeView.connect('row-activated', self.treeActivateCb) + self.treeView.connect('row-activated', self.treeActivateCB) self.treeViewSelection = self.treeView.get_selection() - self.treeViewSelection.connect("changed", self.treeSelectCb) + self.treeViewSelection.connect("changed", self.treeSelectCB) # set up the canvas - self.diskStripeGraph = DiskStripeGraph(self.tree, self.editCb) + self.diskStripeGraph = DiskStripeGraph(self.tree, self.editCB) # do the initial population of the tree and the graph self.populate(initial = 1) diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index 5697d2b1a..97d45d267 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -28,10 +28,9 @@ import datacombo import iutil from constants import * -from fsset import * from partIntfHelpers import * -from partRequests import * from partedUtils import * +from storage.formats import device_formats, getFormat import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -72,13 +71,23 @@ def createAlignedLabel(text): return label +defaultMountPoints = ['/', '/boot', '/home', '/tmp', '/usr', + '/var', '/usr/local', '/opt'] + +if iutil.isS390(): + # Many s390 have 2G DASDs, we recomment putting /usr/share on its own DASD + defaultMountPoints.insert(5, '/usr/share') + +if iutil.isEfi(): + defaultMountPoints.insert(2, '/boot/efi') + def createMountPointCombo(request, excludeMountPoints=[]): mountCombo = gtk.combo_box_entry_new_text() mntptlist = [] - - if request.type != REQUEST_NEW and request.fslabel: - mntptlist.append(request.fslabel) + label = getattr(request.format, "label", None) + if request.exists and label.startswith("/"): + mntptlist.append(label) idx = 0 for p in defaultMountPoints: @@ -90,9 +99,8 @@ def createMountPointCombo(request, excludeMountPoints=[]): map(mountCombo.append_text, mntptlist) - mountpoint = request.mountpoint - - if request.fstype and request.fstype.isMountable(): + if request.format.type and request.format.mountable: + mountpoint = request.format.mountpoint mountCombo.set_sensitive(1) if mountpoint: mountCombo.get_children()[0].set_text(mountpoint) @@ -107,13 +115,14 @@ def createMountPointCombo(request, excludeMountPoints=[]): return mountCombo def setMntPtComboStateFromType(fstype, mountCombo): + format = getFormat(fstype) prevmountable = mountCombo.get_data("prevmountable") mountpoint = mountCombo.get_data("saved_mntpt") - if prevmountable and fstype.isMountable(): + if prevmountable and format.mountable: return - if fstype.isMountable(): + if format.mountable: mountCombo.set_sensitive(1) if mountpoint != None: mountCombo.get_children()[0].set_text(mountpoint) @@ -125,7 +134,7 @@ def setMntPtComboStateFromType(fstype, mountCombo): mountCombo.get_children()[0].set_text(_("")) mountCombo.set_sensitive(0) - mountCombo.set_data("prevmountable", fstype.isMountable()) + mountCombo.set_data("prevmountable", format.mountable) def fstypechangeCB(widget, mountCombo): fstype = widget.get_active_value() @@ -134,24 +143,25 @@ def fstypechangeCB(widget, mountCombo): def createAllowedDrivesStore(disks, reqdrives, drivelist, selectDrives=True, disallowDrives=[]): drivelist.clear() - drives = disks.keys() - drives.sort() - for drive in drives: - size = disks[drive].device.getSize(unit="MB") + for disk in disks: selected = 0 if selectDrives: if reqdrives: - if drive in reqdrives: + if disk.name in reqdrives: selected = 1 else: - if drive not in disallowDrives: + if disk.name not in disallowDrives: selected = 1 - sizestr = "%8.0f MB" % size - drivelist.append_row((drive, sizestr, disks[drive].device.model), selected) + sizestr = "%8.0f MB" % disk.size + # TODO: abstract disk model so we don't have to reach into parted.Disk + drivelist.append_row((disk.name, + sizestr, + disk.partedDisk.device.model), + selected) - if len(disks.keys()) < 2: + if len(disks) < 2: drivelist.set_sensitive(False) else: drivelist.set_sensitive(True) @@ -175,31 +185,33 @@ def createFSTypeMenu(fstype, fstypechangeCB, mountCombo, store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) fstypecombo = datacombo.DataComboBox(store) - types = fileSystemTypeGetTypes() + types = device_formats.keys() if availablefstypes: names = availablefstypes else: names = types.keys() - if fstype and fstype.isSupported() and fstype.isFormattable(): + fs = getFormat(fstype) + if fs and fs.supported and fs.formattable: default = fstype else: - default = fileSystemTypeGetDefault() + default = get_default_filesystem_type() names.sort() defindex = 0 i = 0 for name in names: - if not fileSystemTypeGet(name).isSupported(): + format = getFormat(name) + if not format.supported: continue if ignorefs and name in ignorefs: continue - if fileSystemTypeGet(name).isFormattable(): - fstypecombo.append(name, types[name]) - if default and default.getName() == name: + if format.formattable: + fstypecombo.append(name, device_formats[name]) + if default and default.type == name: defindex = i - defismountable = types[name].isMountable() + defismountable = format.mountable i = i + 1 fstypecombo.set_active(defindex) @@ -209,7 +221,7 @@ def createFSTypeMenu(fstype, fstypechangeCB, mountCombo, if mountCombo: mountCombo.set_data("prevmountable", - fstypecombo.get_active_value().isMountable()) + fstypecombo.get_active_value()().mountable) mountCombo.connect("changed", mountptchangeCB, fstypecombo) return fstypecombo @@ -218,7 +230,7 @@ def mountptchangeCB(widget, fstypecombo): if iutil.isEfi() and widget.get_children()[0].get_text() == "/boot/efi": fstypecombo.set_active_text("efi") if widget.get_children()[0].get_text() == "/boot": - fstypecombo.set_active_text(fileSystemTypeGetDefaultBoot().getName()) + fstypecombo.set_active_text(get_default_filesystem_type(boot=True)) def resizeOptionCB(widget, resizesb): resizesb.set_sensitive(widget.get_active()) @@ -282,11 +294,11 @@ def noformatCB(widget, data): def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, partitions, ignorefs=[]): rc = {} - ofstype = origrequest.fstype + ofstype = origrequest.format.type formatcb = gtk.CheckButton(label=_("_Format as:")) maintable.attach(formatcb, 0, 1, row, row + 1) - formatcb.set_active(istruefalse(origrequest.format)) + formatcb.set_active(istruefalse(origrequest.format.exists)) rc["formatcb"] = formatcb fstypeCombo = createFSTypeMenu(ofstype, fstypechangeCB, @@ -296,8 +308,9 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, row += 1 rc["fstypeCombo"] = fstypeCombo - if not formatcb.get_active() and not origrequest.migrate: - mountCombo.set_data("prevmountable", ofstype.isMountable()) + # TODO: sort out fs migration + if not formatcb.get_active() and not origrequest.format.migrate: + mountCombo.set_data("prevmountable", getFormat(ofstype).mountable) # this gets added to the table a bit later on lukscb = gtk.CheckButton(_("_Encrypt")) @@ -306,11 +319,12 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, (fstypeCombo, mountCombo, ofstype, lukscb)) - if origrequest.origfstype.isMigratable(): + if origrequest.format.migratable: migratecb = gtk.CheckButton(label=_("Mi_grate filesystem to:")) - migratecb.set_active(istruefalse(origrequest.migrate)) + migratecb.set_active(istruefalse(origrequest.format.migrate)) - migtypes = origrequest.origfstype.getMigratableFSTargets() + # TODO: unimplemented + migtypes = origrequest.format.migrationTargets maintable.attach(migratecb, 0, 1, row, row + 1) migfstypeCombo = createFSTypeMenu(ofstype, None, None, @@ -326,7 +340,7 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, migratecb = None migfstypeCombo = None - if origrequest.isResizable(partitions): + if origrequest.resizable: resizecb = gtk.CheckButton(label=_("_Resize")) resizecb.set_active(origrequest.targetSize is not None) rc["resizecb"] = resizecb @@ -337,7 +351,8 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, else: value = origrequest.size - reqlower = origrequest.getMinimumResizeMB(partitions) + # TODO: sort out resizing + reqlower = origrequest.getMinimumResizeMB(storage) requpper = origrequest.getMaximumResizeMB(partitions) if not origrequest.format: lower = reqlower @@ -357,7 +372,7 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, formatcb.connect("toggled", formatOptionResizeCB, resizesb) - if origrequest.encryption: + if origrequest.format.type == "luks": lukscb.set_active(1) lukscb.set_data("encrypted", 1) else: diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index 21cb2bcb8..8b6ed5e55 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -28,10 +28,7 @@ import gtk import datacombo import gui -from fsset import * from raid import availRaidLevels -from cryptodev import LUKSDevice -from partRequests import * from partition_ui_helpers_gui import * from constants import * @@ -56,18 +53,17 @@ class RaidEditor: tempDevList = [] if not self.isNew: # We need this list if we are editing. - for id in reqraidpart: - tempDevList.append(self.partitions.getRequestByID(id).device) + for dev in reqraidpart: + tempDevList.append(dev) partrow = 0 - for part, size, used in allraidparts: - partname = "%s" % part - partsize = "%8.0f MB" % size + for part in allraidparts: + partname = "%s" % part.name + partsize = "%8.0f MB" % part.size if self.isNew: partlist.append_row((partname, partsize), False) else: - # Ask self.partitions what devices to list as selected. if part in tempDevList: #list the partition and put it as selected partlist.append_row((partname, partsize), True) @@ -117,7 +113,7 @@ class RaidEditor: numparts = sparesb.get_data("numparts") maxspares = raid.get_raid_max_spares(raidlevel, numparts) - if maxspares > 0 and raidlevel != "RAID0": + if maxspares > 0 and raidlevel != "raid0": adj = sparesb.get_adjustment() value = adj.value if adj.value > maxspares: @@ -145,20 +141,8 @@ class RaidEditor: self.destroy() return None - # read out UI into a partition specification - request = copy.copy(self.origrequest) - request.encryption = copy.deepcopy(self.origrequest.encryption) - - # doesn't make sense for RAID device - if not self.origrequest.getPreExisting(): - filesystem = self.fstypeCombo.get_active_value() - request.fstype = filesystem - - if request.fstype.isMountable(): - request.mountpoint = self.mountCombo.get_children()[0].get_text() - else: - request.mountpoint = None - + actions = [] + luksdev = None raidmembers = [] model = self.raidlist.get_model() iter = model.get_iter_first() @@ -167,82 +151,75 @@ class RaidEditor: part = model.get_value(iter, 1) if val: - req = self.partitions.getRequestByDeviceName(part) - raidmembers.append(req.uniqueID) + dev = self.storage.devicetree.getDeviceByName(part) + raidmembers.append(dev) iter = model.iter_next(iter) - if not self.origrequest.getPreExisting(): - request.raidminor = int(self.minorCombo.get_active_value()) + if not self.origrequest.exists: + # new device + fmt_class = self.fstypeCombo.get_active_value() + mountpoint = self.mountCombo.get_children()[0].get_text() + raidminor = int(self.minorCombo.get_active_value()) - request.raidmembers = raidmembers model = self.levelcombo.get_model() - request.raidlevel = model[self.levelcombo.get_active()][0] + raidlevel = model[self.levelcombo.get_active()][0] - if request.raidlevel != "RAID0": + if raidlevel != "RAID0": self.sparesb.update() - request.raidspares = self.sparesb.get_value_as_int() + spares = self.sparesb.get_value_as_int() else: - request.raidspares = 0 - - if self.formatButton: - request.format = self.formatButton.get_active() - else: - request.format = 0 - - if self.lukscb and self.lukscb.get_active(): - if not request.encryption: - request.encryption = LUKSDevice(passphrase=self.partitions.encryptionPassphrase, format=1) - else: - request.encryption = None + spares = 0 + + format = fmt_class(mountpoint=mountpoint) + if self.fsoptionsDict.has_key("lukscb") and \ + self.fsoptionsDict["lukscb"].get_active() and \ + self.origrequest.format.type != "luks": + luksdev = LUKSDevice("luks-%s" % request.name, + format=format + parents=self.origrequest) + format = getFormat("luks", + passphrase=self.storage.encryptionPassphrase) else: - if self.fsoptionsDict.has_key("formatcb"): - request.format = self.fsoptionsDict["formatcb"].get_active() - if request.format: - request.fsystem = self.fsoptionsDict["fstypeCombo"].get_active_value() - else: - request.format = 0 - - if self.fsoptionsDict.has_key("migratecb"): - request.migrate = self.fsoptionsDict["migratecb"].get_active() - if request.migrate: - request.fsystem = self.fsoptionsDict["migfstypeCombo"].get_active_value() - else: - request.migrate = 0 - - # set back if we are not formatting or migrating - origfstype = self.origrequest.origfstype - if not request.format and not request.migrate: - request.fstype = origfstype - - if request.fstype.isMountable(): - request.mountpoint = self.mountCombo.get_children()[0].get_text() - else: - request.mountpoint = None - - lukscb = self.fsoptionsDict.get("lukscb") - if lukscb and lukscb.get_active(): - if not request.encryption: - request.encryption = LUKSDevice(passphrase=self.partitions.encryptionPassphrase, format=1) - else: - request.encryption = None - - err = request.sanityCheckRequest(self.partitions) + # existing device + fmt_class = self.fsoptionsDict["fstypeCombo"].get_active_value() + mountpoint = self.mountCombo.get_children()[0].get_text() + if self.fsoptionsDict.has_key("formatcb") and \ + self.fsoptionsDict["formatcb"].get_active(): + format = fmt_class(mountpoint=mountpoint) + if self.fsoptionsDict.has_key("lukscb") and \ + self.fsoptionsDict["lukscb"].get_active() and \ + request.format.type != "luks": + luksdev = LUKSDevice("luks-%s" % request.name, + format=format, + parents=request) + format = getFormat("luks", + device=self.origrequest.path, + passphrase=self.storage.encryptionPassphrase) + + if self.fsoptionsDict.has_key("migratecb") and \ + self.fsoptionsDict["migratecb"].get_active(): + fstype = self.fsoptionsDict["migfstypeCombo"].get_active_value() + + err = self.storage.sanityCheckRequest(request) if err: self.intf.messageWindow(_("Error With Request"), "%s" % (err), custom_icon="error") continue - if (not request.format and - request.mountpoint and request.formatByDefault()): + if request.format.exists and \ + self.storage.formatByDefault(request): if not queryNoFormatPreExisting(self.intf): continue # everything ok, break out break + actions.append(ActionCreateDevice(self.origrequest)) + if luksdev: + actions.append(ActionCreateDevice(luksdev)) - return request + return actions def destroy(self): if self.dialog: @@ -250,9 +227,8 @@ class RaidEditor: self.dialog = None - def __init__(self, partitions, diskset, intf, parent, origrequest, isNew = 0): - self.partitions = partitions - self.diskset = diskset + def __init__(self, storage, intf, parent, origrequest, isNew = 0): + self.storage = storage self.origrequest = origrequest self.isNew = isNew self.intf = intf @@ -263,8 +239,8 @@ class RaidEditor: # # start of editRaidRequest # - availraidparts = self.partitions.getAvailRaidPartitions(origrequest, - self.diskset) + availraidparts = self.storage.unusedMDMembers(array=self.origrequest) + # if no raid partitions exist, raise an error message and return if len(availraidparts) < 2: dlg = gtk.MessageDialog(self.parent, 0, gtk.MESSAGE_ERROR, @@ -285,8 +261,8 @@ class RaidEditor: if isNew: tstr = _("Make RAID Device") else: - try: - tstr = _("Edit RAID Device: /dev/md%s") % (origrequest.raidminor,) + if origrequest.minor is not None: + tstr = _("Edit RAID Device: %s") % (origrequest.path,) except: tstr = _("Edit RAID Device") @@ -313,11 +289,18 @@ class RaidEditor: self.lukscb = gtk.CheckButton(_("_Encrypt")) self.lukscb.set_data("formatstate", 1) + if origrequest.format.type == "luks": + usedev = self.storage.devicetree.getChildren(origrequest)[0] + format = usedev.format + else: + usedev = origrequest + format = origrequest.format + # Filesystem Type - if not origrequest.getPreExisting(): + if not origrequest.exists: lbl = createAlignedLabel(_("_File System Type:")) maintable.attach(lbl, 0, 1, row, row + 1) - self.fstypeCombo = createFSTypeMenu(origrequest.fstype, + self.fstypeCombo = createFSTypeMenu(format.name, fstypechangeCB, self.mountCombo, ignorefs = ["software RAID", "efi", "PPC PReP Boot", "Apple Bootstrap"]) @@ -327,29 +310,29 @@ class RaidEditor: else: maintable.attach(createAlignedLabel(_("Original File System Type:")), 0, 1, row, row + 1) - if origrequest.fstype.getName(): - self.fstypeCombo = gtk.Label(origrequest.fstype.getName()) + if format.type: + self.fstypeCombo = gtk.Label(format.name) else: self.fstypeCombo = gtk.Label(_("Unknown")) maintable.attach(self.fstypeCombo, 1, 2, row, row + 1) row += 1 - if origrequest.fslabel: + if getattr(format, "label", None): maintable.attach(createAlignedLabel(_("Original File System " "Label:")), 0, 1, row, row + 1) - maintable.attach(gtk.Label(origrequest.fslabel), 1, 2, row, - row + 1) + maintable.attach(gtk.Label(format.label), + 1, 2, row, row + 1) row += 1 # raid minors lbl = createAlignedLabel(_("RAID _Device:")) maintable.attach(lbl, 0, 1, row, row + 1) - if not origrequest.getPreExisting(): - availminors = self.partitions.getAvailableRaidMinors()[:16] - reqminor = origrequest.raidminor + if not origrequest.exists: + availminors = self.storage.unusedMDMinors()[:16] + reqminor = origrequest.minor if reqminor is not None: availminors.append(reqminor) @@ -357,7 +340,7 @@ class RaidEditor: self.minorCombo = self.createRaidMinorMenu(availminors, reqminor) lbl.set_mnemonic_widget(self.minorCombo) else: - self.minorCombo = gtk.Label("md%s" %(origrequest.raidminor,)) + self.minorCombo = gtk.Label("%s" %(origrequest.name,)) maintable.attach(self.minorCombo, 1, 2, row, row + 1) row = row + 1 @@ -365,16 +348,17 @@ class RaidEditor: lbl = createAlignedLabel(_("RAID _Level:")) maintable.attach(lbl, 0, 1, row, row + 1) - if not origrequest.getPreExisting(): + if not origrequest.exists: # Create here, pack below numparts = len(availraidparts) - if origrequest.raidspares: - nspares = origrequest.raidspares + if origrequest.spares: + nspares = origrequest.spares else: nspares = 0 - if origrequest.raidlevel: - maxspares = raid.get_raid_max_spares(origrequest.raidlevel, numparts) + if origrequest.level: + maxspares = raid.get_raid_max_spares(origrequest.level, + numparts) else: maxspares = 0 @@ -389,15 +373,15 @@ class RaidEditor: self.sparesb.set_value(0) self.sparesb.set_sensitive(0) else: - self.sparesb = gtk.Label(str(origrequest.raidspares)) + self.sparesb = gtk.Label(str(origrequest.spares)) - if not origrequest.getPreExisting(): + if not origrequest.exists: self.levelcombo = self.createRaidLevelMenu(availRaidLevels, - origrequest.raidlevel) + origrequest.level) lbl.set_mnemonic_widget(self.levelcombo) else: - self.levelcombo = gtk.Label(origrequest.raidlevel) + self.levelcombo = gtk.Label(origrequest.level) maintable.attach(self.levelcombo, 1, 2, row, row + 1) row = row + 1 @@ -408,15 +392,15 @@ class RaidEditor: # XXX need to pass in currently used partitions for this device (self.raidlist, sw) = self.createAllowedRaidPartitionsList(availraidparts, - origrequest.raidmembers, - origrequest.getPreExisting()) + origrequest.devices, + origrequest.exists) lbl.set_mnemonic_widget(self.raidlist) self.raidlist.set_size_request(275, 80) maintable.attach(sw, 1, 2, row, row + 1) row = row + 1 - if origrequest.getPreExisting(): + if origrequest.exists: self.raidlist.set_sensitive(False) # number of spares - created widget above @@ -429,26 +413,26 @@ class RaidEditor: # format or not? self.formatButton = None self.fsoptionsDict = {} - if (origrequest.fstype and origrequest.fstype.isFormattable()) and not origrequest.getPreExisting(): + if not format.exists: self.formatButton = gtk.CheckButton(_("_Format partition?")) - if origrequest.format == None or origrequest.format != 0: + if not format.type: self.formatButton.set_active(1) else: self.formatButton.set_active(0) # it only makes sense to show this for preexisting RAID - if origrequest.getPreExisting(): + if origrequest.exists: maintable.attach(self.formatButton, 0, 2, row, row + 1) row = row + 1 # checkbutton for encryption using dm-crypt/LUKS - if self.origrequest.encryption: + if origrequest.format.type == "luks": self.lukscb.set_active(1) else: self.lukscb.set_active(0) maintable.attach(self.lukscb, 0, 2, row, row + 1) row = row + 1 else: - (row, self.fsoptionsDict) = createPreExistFSOptionSection(self.origrequest, maintable, row, self.mountCombo, self.partitions) + (row, self.fsoptionsDict) = createPreExistFSOptionSection(usedev, maintable, row, self.mountCombo, self.storage) # put main table into dialog dialog.vbox.pack_start(maintable) @@ -460,7 +444,7 @@ class RaidEditor: class RaidCloneDialog: - def createDriveList(self, diskset): + def createDriveList(self, disks): store = gtk.ListStore(gobject.TYPE_STRING) view = gtk.TreeView(store) @@ -470,12 +454,9 @@ class RaidCloneDialog: sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) sw.set_shadow_type(gtk.SHADOW_IN) - drives = diskset.disks.keys() - drives.sort() - - for drive in drives: + for disk in disks: iter = store.append() - store.set_value(iter, 0, drive) + store.set_value(iter, 0, disk.name) view.set_property("headers-visible", False) @@ -485,27 +466,18 @@ class RaidCloneDialog: return (sw, view) def getInterestingRequestsForDrive(self, drive): - allrequests = self.partitions.getRequestsByDevice(self.diskset, drive) + disk = self.storage.devicetree.getDeviceByName(drive) + allrequests = self.storage.getDependentDevices(disk) - if allrequests is None or len(allrequests) == 0: + if not allrequests: return allrequests # remove extended partitions requests = [] for req in allrequests: - try: - part = parted.getPartitionByName(req.device) - except: - part = None - - if part: - if part.type & parted.PARTITION_EXTENDED: - continue - elif part.type & parted.PARTITION_FREESPACE: - continue - elif part.type & parted.PARTITION_METADATA: - continue - else: + if req.type == "partition" and req.isExtended: + continue + elif req.type != "partition": continue requests.append(req) @@ -525,7 +497,7 @@ class RaidCloneDialog: return 1 for req in requests: - if not req.fstype or req.fstype.getName() != "software RAID": + if req.format.type != "mdmember": self.intf.messageWindow(_("Source Drive Error"), _("The source drive you selected has " "partitions which are not of " @@ -536,21 +508,23 @@ class RaidCloneDialog: custom_icon="error") return 1 + sourceDev = self.storage.devicetree.getDeviceByName(self.sourceDrive) for req in requests: - if not req.drive or req.drive[0] != self.sourceDrive or len(req.drive) > 1: + if not req.req_disks or len(req.req_disks) > 1 or \ + req.req_disks[0] != self.sourceDrive: self.intf.messageWindow(_("Source Drive Error"), _("The source drive you selected has " "partitions which are not " - "constrained to the drive /dev/%s.\n\n" + "constrained to the drive %s.\n\n" "You must remove these partitions " "or restrict them to this " "drive " "before this drive can be cloned. ") - %(self.sourceDrive,), custom_icon="error") + %(sourceDev.path,), custom_icon="error") return 1 for req in requests: - if self.partitions.isRaidMember(req): + if req not in self.storage.unusedMDMembers(): self.intf.messageWindow(_("Source Drive Error"), _("The source drive you selected has " "software RAID partition(s) which " @@ -564,6 +538,7 @@ class RaidCloneDialog: return 0 def sanityCheckTargetDrives(self): + sourceDev = self.storage.devicetree.getDeviceByName(self.sourceDrive) if self.targetDrives is None or len(self.targetDrives) < 1: self.intf.messageWindow(_("Target Drive Error"), _("Please select the target drives " @@ -572,8 +547,10 @@ class RaidCloneDialog: if self.sourceDrive in self.targetDrives: self.intf.messageWindow(_("Target Drive Error"), - _("The source drive /dev/%s cannot be " - "selected as a target drive as well.") % (self.sourceDrive,), custom_icon="error") + _("The source drive %s cannot be " + "selected as a target drive as well.") + % (self.sourceDev.path,), + custom_icon="error") return 1 for drive in self.targetDrives: @@ -581,24 +558,20 @@ class RaidCloneDialog: if requests is None: continue + targetDev = self.storage.devicetree.getDeviceByName(drive) for req in requests: - rc = partIntfHelpers.isNotChangable(req, self.partitions) - - # If the partition is protected, we also can't delete it so - # specify a reason why. - if rc is None and req.getProtected(): - rc = _("This partition is holding the data for the hard " - "drive install.") + rc = self.storage.deviceImmutable(req) if rc: self.intf.messageWindow(_("Target Drive Error"), - _("The target drive /dev/%s " + _("The target drive %s " "has a partition which cannot " "be removed for the following " "reason:\n\n\"%s\"\n\n" "You must remove this partition " "before " "this drive can be a target.") % - (drive, rc), custom_icon="error") + (targetDev.path, rc), + custom_icon="error") return 1 return 0 @@ -609,27 +582,28 @@ class RaidCloneDialog: requests = self.getInterestingRequestsForDrive(self.sourceDrive) # no requests to clone, bail out - if requests is None or len(requests) == 0: + if not requests: return 0 # now try to clear the target drives - for device in self.targetDrives: - rc = doDeletePartitionsByDevice(self.intf, self.partitions, - self.diskset, device, - confirm=0, quiet=1) + for devname in self.targetDrives: + device = self.storage.devicetree.getDeviceByName(devname) + doDeleteDependentDevices(self.intf, self.storage, + device, confirm=0, quiet=1) # now clone! for req in requests: for drive in self.targetDrives: - newreq = copy.copy(req) - newreq.drive = [drive] - newreq.uniqueID = None - newreq.device = None - newreq.preexist = 0 - newreq.dev = None - self.partitions.addRequest(newreq) + # this feels really dirty + device = self.storage.devicetree.getDeviceByName(drive) + newdev = copy.deepcopy(req) + newdev.req_disks = [device] + newdev.exists = False + newdev.format.exists = False + newdev.format.device = None + self.storage.createDevice(newdev) - return 0 + return def targetSelectFunc(self, model, path, iter): @@ -672,10 +646,10 @@ class RaidCloneDialog: continue # now give them last chance to bail - msgtxt = _("The drive /dev/%s will now be cloned to the " + msgtxt = _("The drive %s will now be cloned to the " "following drives:\n\n" % (self.sourceDrive,)) for drive in self.targetDrives: - msgtxt = msgtxt + "\t" + "/dev/%s" % (drive,) + msgtxt = msgtxt + "\t" + "%s" % (drive,) msgtxt = msgtxt + _("\n\nWARNING! ALL DATA ON THE TARGET DRIVES " "WILL BE DESTROYED.") @@ -708,9 +682,8 @@ class RaidCloneDialog: self.dialog = None - def __init__(self, partitions, diskset, intf, parent): - self.partitions = partitions - self.diskset = diskset + def __init__(self, storage, intf, parent): + self.storage = storage self.intf = intf self.parent = parent @@ -748,7 +721,7 @@ class RaidCloneDialog: lbl = gtk.Label(_("Source Drive:")) lbl.set_alignment(0.0, 0.0) box.pack_start(lbl, padding=5) - (sw, self.sourceView) = self.createDriveList(diskset) + (sw, self.sourceView) = self.createDriveList(storage.disks) selection = self.sourceView.get_selection() selection.set_mode(gtk.SELECTION_SINGLE) box.pack_start(sw) @@ -756,7 +729,7 @@ class RaidCloneDialog: lbl = gtk.Label(_("Target Drive(s):")) lbl.set_alignment(0.0, 0.0) box.pack_start(lbl, padding=5) - (sw, self.targetView) = self.createDriveList(diskset) + (sw, self.targetView) = self.createDriveList(storage.disks) selection = self.targetView.get_selection() selection.set_mode(gtk.SELECTION_MULTIPLE) box.pack_start(sw) diff --git a/partIntfHelpers.py b/partIntfHelpers.py index 13c87220d..9393cd267 100644 --- a/partIntfHelpers.py +++ b/partIntfHelpers.py @@ -26,11 +26,8 @@ import string from constants import * -import partedUtils import parted -import fsset import iutil -import partRequests import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -106,331 +103,98 @@ def sanityCheckMountPoint(mntpt, fstype, preexisting, format): else: return None else: - if (fstype and fstype.isMountable() and (not preexisting or format)): + if (fstype and fstype.mountable and (not preexisting or format)): return _("Please specify a mount point for this partition.") else: # its an existing partition so don't force a mount point return None -def isNotChangable(request, requestlist): - if request: - if requestlist.isRaidMember(request): - parentreq = requestlist.getRaidMemberParent(request) - if parentreq.raidminor is not None: - return _("This partition is part of " - "the RAID device /dev/md%s.") % (parentreq.raidminor,) - else: - return _("This partition is part of a RAID device.") - - if requestlist.isLVMVolumeGroupMember(request): - parentreq = requestlist.getLVMVolumeGroupMemberParent(request) - if parentreq.volumeGroupName is not None: - return _("This partition is part of the " - "LVM volume group '%s'.") % (parentreq.volumeGroupName,) - else: - return _("This partition is part of a LVM volume group.") - - return None - - -def doDeletePartitionByRequest(intf, requestlist, partition, - confirm=1, quiet=0): +def doDeleteDevice(intf, storage, device, confirm=1, quiet=0): """Delete a partition from the request list. intf is the interface - requestlist is the list of requests - partition is either the part object or the uniqueID if not a part + storage is the storage instance + device is the device to delete """ - - if partition == None: + if not device: intf.messageWindow(_("Unable To Delete"), _("You must first select a partition to delete."), custom_icon="error") - return 0 + return False - if type(partition) == type("RAID"): - device = partition - elif partition.type & parted.PARTITION_FREESPACE: - intf.messageWindow(_("Unable To Delete"), - _("You cannot delete free space."), - custom_icon="error") - return 0 - elif partition.type & parted.PARTITION_PROTECTED: - # LDL formatted DASDs always have one partition, you'd have to reformat the - # DASD in CDL mode to get rid of it + reason = storage.deviceImmutable(device) + if reason: intf.messageWindow(_("Unable To Delete"), - _("You cannot delete a partition of a LDL formatted DASD."), - custom_icon="error") - return 0 - else: - device = partition.getDeviceNodeName() - - ret = requestlist.containsImmutablePart(partition) - if ret: - if not quiet: - intf.messageWindow(_("Unable To Delete"), - _("You cannot delete this " - "partition, as it is an extended partition " - "which contains %s") %(ret), - custom_icon="error") - return 0 - - # see if device is in our partition requests, remove - if type(partition) == type("RAID"): - request = requestlist.getRequestByID(device) - else: - request = requestlist.getRequestByDeviceName(device) - - if request: - state = isNotChangable(request, requestlist) - - # If the partition is protected, we also can't delete it so specify a - # reason why. - if state is None and request.getProtected(): - state = _("This partition is holding the data for the hard " - "drive install.") - - if state: - if not quiet: - intf.messageWindow(_("Unable To Delete"), - _("You cannot delete this partition:\n\n") + state, - custom_icon="error") - return 0 - - if confirm and not confirmDeleteRequest(intf, request): - return 0 - - if request.getPreExisting(): - if isinstance(request, partRequests.PartitionSpec): - # get the drive - drive = partedUtils.get_partition_drive(partition) - - if partition.type & parted.PARTITION_EXTENDED: - requestlist.deleteAllLogicalPartitions(partition) - - delete = partRequests.DeleteSpec(drive, - partition.geometry.start, - partition.geometry.end) - requestlist.addDelete(delete) - elif isinstance(request, partRequests.LogicalVolumeRequestSpec): - vgreq = requestlist.getRequestByID(request.volumeGroup) - delete = partRequests.DeleteLogicalVolumeSpec(request.logicalVolumeName, - vgreq.volumeGroupName) - requestlist.addDelete(delete) - elif isinstance(request, partRequests.VolumeGroupRequestSpec): - delete = partRequests.DeleteVolumeGroupSpec(request.volumeGroupName) - requestlist.addDelete(delete) - # FIXME: do we need to do anything with preexisting raids? - - # now remove the request - requestlist.deleteDependentRequests(request) - requestlist.removeRequest(request) - else: # is this a extended partition we made? - if partition.type & parted.PARTITION_EXTENDED: - requestlist.deleteAllLogicalPartitions(partition) - else: - #raise ValueError, "Deleting a non-existent partition" - return 0 + reason, + customer_icon="error") + return False + + if confirm and not confirmDelete(intf, device): + return False - del partition - return 1 + for dep in storage.deviceDeps(device): + storage.deleteDevice(dep) -def doDeletePartitionsByDevice(intf, requestlist, diskset, device, - confirm=1, quiet=0): - """ Remove all partitions currently on device """ + storage.deleteDevice(device) + return True + +def doDeleteDependentDevices(intf, storage, device, confirm=1, quiet=0): + """ Remove all devices/partitions currently on device """ if confirm: rc = intf.messageWindow(_("Confirm Delete"), _("You are about to delete all partitions on " - "the device '/dev/%s'.") % (device,), + "the device '%s'.") % (device.path,), type="custom", custom_icon="warning", custom_buttons=[_("Cancel"), _("_Delete")]) if not rc: - return - - requests = requestlist.getRequestsByDevice(diskset, device) - if not requests: - return - - # get list of unique IDs of these requests - reqIDs = set() - reqparts = {} - - for req in requests: - for drive in req.drive: - part = diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device) - - if part.type & parted.PARTITION_FREESPACE or \ - part.type & parted.PARTITION_METADATA or \ - part.type & parted.PARTITION_PROTECTED: - continue - - reqIDs.add(req.uniqueID) - - if reqparts.has_key(req.uniqueID): - reqparts[req.uniqueID].append(part) - else: - reqparts[req.uniqueID] = [ part ] - - reqIDs = list(reqIDs) - - # now go thru and try to delete the unique IDs - for id in reqIDs: - try: - req = requestlist.getRequestByID(id) - if req is None: - continue - for partlist in reqparts[id]: - for part in partlist: - rc = doDeletePartitionByRequest(intf, requestlist, part, - confirm=0, quiet=1) - if not rc: - pass - except: - pass - - # see which partitions are left - notdeleted = [] - left_requests = requestlist.getRequestsByDevice(diskset, device) - if left_requests: - # get list of unique IDs of these requests - leftIDs = set() - - for req in left_requests: - for drive in req.drive: - part = diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device) - - if part.type & parted.PARTITION_FREESPACE or \ - part.type & parted.PARTITION_METADATA or \ - part.type & parted.PARTITION_PROTECTED: - continue - - leftIDs.add(req.uniqueID) - - leftIDs = list(leftIDs) - - for id in leftIDs: - req = requestlist.getRequestByID(id) - notdeleted.append(req) - - # see if we need to report any failures - some were because we removed - # an extended partition which contained other members of our delete list - outlist = "" - for req in notdeleted: - newreq = requestlist.getRequestByID(req.uniqueID) - if newreq: - outlist = outlist + "\t/dev/%s\n" % (newreq.device,) - - if outlist != "" and not quiet: + return False + + deps = storage.deviceDeps(device) + if not deps: + # nothing to do + return False + + immmutable = [] + for dep in deps: + if storage.deviceImmutable(dep): + immutable.append(dep.path) + continue + else: + storage.deleteDevice(dep) + + if immutable and not quiet: + remaining = "\n\t" + "\n\t".join(immutable) + "\n" intf.messageWindow(_("Notice"), _("The following partitions were not deleted " "because they are in use:\n\n%s") % outlist, custom_icon="warning") - return 1 - - -def doEditPartitionByRequest(intf, requestlist, part): - """Edit a partition from the request list. - - intf is the interface - requestlist is the list of requests - partition is either the part object or the uniqueID if not a part - """ - - if part == None: - intf.messageWindow(_("Unable To Edit"), - _("You must select a partition to edit"), custom_icon="error") - - return (None, None) - - if type(part) == type("RAID"): - - # see if device is in our partition requests, remove - request = requestlist.getRequestByID(int(part)) - - if request: - state = isNotChangable(request, requestlist) - if state is not None: - intf.messageWindow(_("Unable To Edit"), _("You cannot edit this partition:\n\n") + state, - custom_icon="error") - return (None, None) - - if request.type == REQUEST_RAID: - return ("RAID", request) - elif request.type == REQUEST_VG: - return ("LVMVG", request) - elif request.type == REQUEST_LV: - return ("LVMLV", request) - else: - return (None, None) - elif part.type & parted.PARTITION_FREESPACE: - request = partRequests.PartitionSpec(fsset.fileSystemTypeGetDefault(), - start = part.geometry.device.startSectorToCylinder(part.geometry.start), - end = part.geometry.device.endSectorToCylinder(part.geometry.end), - drive = [ partedUtils.get_partition_drive(part) ]) - - return ("NEW", request) - elif part.type & parted.PARTITION_EXTENDED: - return (None, None) - - ret = requestlist.containsImmutablePart(part) - if ret: - intf.messageWindow(_("Unable To Edit"), - _("You cannot edit this " - "partition, as it is an extended partition " - "which contains %s") %(ret), custom_icon="error") - return (None, None) - - name = part.getDeviceNodeName() - request = requestlist.getRequestByDeviceName(name) - if request: - state = isNotChangable(request, requestlist) - if state is not None: - intf.messageWindow(_("Unable To Edit"), - _("You cannot edit this partition:\n\n") + state, custom_icon="error") - return (None, None) - - return ("PARTITION", request) - else: # shouldn't ever happen - raise ValueError, ("Trying to edit non-existent partition %s" - % (part.getDeviceNodeName(),)) - + return True def checkForSwapNoMatch(anaconda): """Check for any partitions of type 0x82 which don't have a swap fs.""" - diskset = anaconda.id.diskset - - for request in anaconda.id.partitions.requests: - if not hasattr(request, "drive") or not request.fstype: + for device in anaconda.id.storage.partitions: + if not device.exists: + # this is only for existing partitions continue - for drive in request.drive: - part = diskset.disks[drive].getPartitionByPath("/dev/%s" % request.device) - - if (part and (not part.type & parted.PARTITION_FREESPACE) - and (part.getFlag(parted.PARTITION_SWAP)) - and (request.fstype and request.fstype.getName() != "swap") - and (not request.format)): - rc = anaconda.intf.messageWindow(_("Format as Swap?"), - _("/dev/%s has a partition type of 0x82 " - "(Linux swap) but does not appear to " - "be formatted as a Linux swap " - "partition.\n\n" - "Would you like to format this " - "partition as a swap partition?") - % (request.device), type = "yesno", - custom_icon="question") - if rc == 1: - request.format = 1 - request.fstype = fsset.fileSystemTypeGet("swap") - if request.fstype.getName() == "software RAID": - part.setFlag(parted.PARTITION_RAID) - else: - part.unsetFlag(parted.PARTITION_RAID) - - partedUtils.set_partition_file_system_type(part, - request.fstype) + if device.partType & parted.PARTITION_SWAP and \ + not device.format.type == "swap": + rc = anaconda.intf.messageWindow(_("Format as Swap?"), + _("%s has a partition type of 0x82 " + "(Linux swap) but does not appear to " + "be formatted as a Linux swap " + "partition.\n\n" + "Would you like to format this " + "partition as a swap partition?") + % device.path, type = "yesno", + custom_icon="question") + if rc == 1: + format = getFormat("swap", device=device.path) + anaconda.id.storage.formatDevice(device, format) + + return def mustHaveSelectedDrive(intf): txt =_("You need to select at least one hard drive to install %s.") % (productName,) @@ -498,63 +262,42 @@ def partitionPreExistFormatWarnings(intf, warnings): type="yesno", custom_icon="warning") return rc -def getPreExistFormatWarnings(partitions, diskset): - """Return a list of preexisting partitions being formatted.""" - - devs = [] - for request in partitions.requests: - if request.getPreExisting() == 1: - devs.append(request.uniqueID) +def getPreExistFormatWarnings(storage): + """Return a list of preexisting devices being formatted.""" + devices = [] + for device in storage.devicetree.devices.values(): + if device.exists and not device.format.exists: + devices.append(device) - devs.sort() - + devices.sort(key=lambda d: d.name) rc = [] - for dev in devs: - request = partitions.getRequestByID(dev) - if request.format: - if request.fstype.isMountable(): - mntpt = request.mountpoint - else: - mntpt = "" - - if isinstance(request, partRequests.PartitionSpec): - dev = request.device - elif isinstance(request, partRequests.RaidRequestSpec): - dev = "md%s" %(request.raidminor,) - elif isinstance(request, partRequests.VolumeGroupRequestSpec): - dev = request.volumeGroupName - elif isinstance(request, partRequests.LogicalVolumeRequestSpec): - vgreq = partitions.getRequestByID(request.volumeGroup) - dev = "%s/%s" %(vgreq.volumeGroupName, - request.logicalVolumeName) - - rc.append((dev, request.fstype.getName(), mntpt)) - - if len(rc) == 0: - return None - else: - return rc + for device in devices: + rc.append((device.path, + device.format.type, + getattr(device.format, "mountpoint", ""))) + return rc -def confirmDeleteRequest(intf, request): - """Confirm the deletion of a request.""" - if not request: +def confirmDelete(intf, device): + """Confirm the deletion of a device.""" + if not device: return - if request.type == REQUEST_VG: + if device.type == "lvmvg": errmsg = (_("You are about to delete the volume group \"%s\"." "\n\nALL logical volumes in this volume group " - "will be lost!") % (request.volumeGroupName,)) - elif request.type == REQUEST_LV: + "will be lost!") % device.name) + elif device.type == "lvmlv": errmsg = (_("You are about to delete the logical volume \"%s\".") - % (request.logicalVolumeName,)) - elif request.type == REQUEST_RAID: + % (device.name) + elif device.type == "mdarray": errmsg = _("You are about to delete a RAID device.") + elif device.type == "partition": + errmsg = (_("You are about to delete the %s partition.") + % device.path) else: - if request.device: - errmsg = _("You are about to delete the /dev/%s partition.") % (request.device,) - else: - # XXX can this ever happen? - errmsg = _("The partition you selected will be deleted.") + # we may want something a little bit prettier than device.type + errmsg = (_("You are about to delete the %s %s") % (device.type, + device.name)) rc = intf.messageWindow(_("Confirm Delete"), errmsg, type="custom", custom_buttons=[_("Cancel"), _("_Delete")], diff --git a/text.py b/text.py index e518e84f5..b91504056 100644 --- a/text.py +++ b/text.py @@ -629,10 +629,6 @@ class InstallInterface: # draw the frame after setting up the fallback self.drawFrame() - anaconda.id.fsset.registerMessageWindow(self.messageWindow) - anaconda.id.fsset.registerProgressWindow(self.progressWindow) - anaconda.id.fsset.registerWaitWindow(self.waitWindow) - lastrc = INSTALL_OK (step, instance) = anaconda.dispatch.currentStep() while step: -- cgit From 10d9789a3f4c6923c8cff6e83f125afb0cd93957 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 14:01:44 -0600 Subject: Fix whitespace in bindMountDevDirectory. --- upgrade.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/upgrade.py b/upgrade.py index 98979b836..f8e299626 100644 --- a/upgrade.py +++ b/upgrade.py @@ -154,10 +154,10 @@ def findExistingRoots(anaconda, upgradeany = 0): return rootparts def bindMountDevDirectory(instPath): - getFormat("bind", - device="/dev", - mountpoint="/dev", - exists=True).mount(chroot=instPath) + getFormat("bind", + device="/dev", + mountpoint="/dev", + exists=True).mount(chroot=instPath) # returns None if no filesystem exist to migrate def upgradeMigrateFind(anaconda): -- cgit From 3154489f9c458b2dd1eccc55528cd62e0c98c943 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 18:02:37 -0600 Subject: Remove getReleaseString from partedUtils. It will soon reappear in storage.__init__.py. --- partedUtils.py | 44 -------------------------------------------- 1 file changed, 44 deletions(-) diff --git a/partedUtils.py b/partedUtils.py index 66eb1a252..68d87d98a 100644 --- a/partedUtils.py +++ b/partedUtils.py @@ -313,50 +313,6 @@ def isLinuxNative(part): else: return False -def getReleaseString(mountpoint): - relName = None - relVer = None - - if os.access(mountpoint + "/etc/redhat-release", os.R_OK): - f = open(mountpoint + "/etc/redhat-release", "r") - try: - lines = f.readlines() - except IOError: - try: - f.close() - except: - pass - return (relName, relVer) - - f.close() - - # return the first line with the newline at the end stripped - if len(lines) == 0: - return (relName, relVer) - - 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") - relName = relstr[:idx - 1] - relVer = "" - - for a in relstr[idx + 8:]: - if a in string.digits + ".": - relVer += a - else: - break - - relstr = prod + " " + ver - except: - pass # don't worry, just use the relstr as we have it - - return (relName, relVer) - class DiskSet: """The disks in the system.""" -- cgit From 8c6b7c6e9aac29e71e87897aab861c570427ade5 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 18:04:02 -0600 Subject: Add some useful functions to iutil. --- iutil.py | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/iutil.py b/iutil.py index f13956f8a..7bedf25bf 100644 --- a/iutil.py +++ b/iutil.py @@ -23,6 +23,7 @@ import os, string, stat, sys import os.path from errno import * +import inspect import rhpl import warnings import subprocess @@ -660,3 +661,54 @@ def strip_markup(text): elif not inTag: r += c return r.encode("utf-8") + +def notify_kernel(path, action="change"): + """ Signal the kernel that the specified device has changed. """ + log.debug("notifying kernel of '%s' event on device %s" % (action, path)) + path = os.path.join(path, "uevent") + if not path.startswith("/sys/") or not os.access(path, os.W_OK): + log.debug("sysfs path '%s' invalid" % path) + raise ValueError("invalid sysfs path") + + f = open(path, "a") + f.write("%s\n" % action) + f.close() + +def get_sysfs_path_by_name(dev_name, class_name="block"): + dev_name = os.path.basename(dev_name) + sysfs_class_dir = "/sys/class/%s" % class_name + dev_path = os.path.join(sysfs_class_dir, dev_name) + if os.path.exists(dev_path): + return dev_path + +def log_method_call(d, *args, **kwargs): + classname = d.__class__.__name__ + methodname = inspect.stack()[1][3] + fmt = "%s.%s:" + fmt_args = [classname, methodname] + for arg in args: + fmt += " %s ;" + fmt_args.append(arg) + + for k, v in kwargs.items(): + fmt += " %s: %s ;" + fmt_args.extend([k, v]) + + log.debug(fmt % tuple(fmt_args)) + +def numeric_type(num): + """ Verify that a value is given as a numeric data type. + + Return the number if the type is sensible or raise ValueError + if not. + """ + if num is None: + num = 0 + elif not (isinstance(num, int) or \ + isinstance(num, long) or \ + isinstance(num, float)): + raise ValueError("value (%s) must be either a number or None" % num) + + return num + + -- cgit From d3f05baaa850eac98c220c04559c2f307fa95539 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 18:05:36 -0600 Subject: Add getReleaseString, fix several typos. --- storage/__init__.py | 74 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 20 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index cd890fb2d..6fccba6d8 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -23,18 +23,11 @@ import os import time -import sys -sys.path.append("formats") - -if __name__ == "__main__": - import storage_log - import iutil -from partedUtils import getReleaseString, productMatches from errors import * from devices import * from deviceaction import * -from deviceformat import getFormat +from formats import getFormat from devicelibs.lvm import safeLvmName import gettext @@ -240,7 +233,7 @@ class Storage(object): arrays.sort(key=lambda d: d.name) return arrays - @properties + @property def mdmembers(self): """ A list of the MD member devices in the device tree. @@ -509,6 +502,50 @@ class Storage(object): return tmpname +def getReleaseString(mountpoint): + relName = None + relVer = None + + if os.access(mountpoint + "/etc/redhat-release", os.R_OK): + f = open(mountpoint + "/etc/redhat-release", "r") + try: + lines = f.readlines() + except IOError: + try: + f.close() + except: + pass + return (relName, relVer) + + f.close() + + # return the first line with the newline at the end stripped + if len(lines) == 0: + return (relName, relVer) + + 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") + relName = relstr[:idx - 1] + relVer = "" + + for a in relstr[idx + 8:]: + if a in string.digits + ".": + relVer += a + else: + break + + relstr = prod + " " + ver + except: + pass # don't worry, just use the relstr as we have it + + return (relName, relVer) + def findExistingRootDevices(anaconda, upgradeany=False): """ Return a list of all root filesystems in the device tree. """ rootDevs = [] @@ -856,7 +893,7 @@ class FSSet(object): parents=get_containing_device(devspec), format=getFormat(fstype, device=devspec, - exists=True) + exists=True), exists=True) elif fstype == "bind" or "bind" in options: # bind mount... set fstype so later comparison won't @@ -954,7 +991,7 @@ class FSSet(object): "which means your system is hibernating. " "To perform an upgrade, please shut down " "your system rather than hibernating it.") \ - % device.path) + % device.path else: msg = _("The swap device:\n\n %s\n\n" "in your /etc/fstab file is currently in " @@ -963,7 +1000,7 @@ class FSSet(object): "If you are performing a new install, " "make sure the installer is set " "to format all swap devices.") \ - % device.path) + % device.path intf.messageWindow(_("Error"), msg) sys.exit(0) @@ -1086,7 +1123,7 @@ class FSSet(object): """ Create and activate a swap file under rootPath. """ filename = "/SWAP" count = 0 - while os.path.exists("%s/%s" % (rootPath, filename) or \ + while os.path.exists("%s/%s" % (rootPath, filename)) or \ self.devicetree.getDeviceByName(filename): file = os.path.normpath("%s/%s" % (rootPath, filename)) count += 1 @@ -1207,17 +1244,14 @@ class FSSet(object): options = device.format.options else: mountpoint = device.format.mountpoint - options = gettattr(device.format.mountopts + options = device.format.mountopts if not mountpoint: log.warning("%s filesystem on %s has no mountpoint" % \ (fstype, device.path)) continue - if not options: - # why not, right? - options = "defaults" - + options = options or "defaults" devspec = device.fstabSpec dump = device.format.dump if device.format.check and mountpoint == "/": @@ -1280,8 +1314,8 @@ class FSSet(object): ret['boot'] = (bootDev.path, N_("Apple Bootstrap")) partitions = self.devicetree.getDevicesByType("partition") for (n, device) in enumerate(partitions): - if (device.format.type == "hfs" and device.bootable and \ - device.path != bootDev.path)): + if device.format.type == "hfs" and device.bootable and \ + device.path != bootDev.path: ret['boot%d' %(n,)] = (device.path, N_("Apple Bootstrap")) elif (iutil.getPPCMachine() == "pSeries" or -- cgit From ad3af83c337caaecbf4041cbb32c9b32df620100 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 18:10:46 -0600 Subject: Clean up imports and various typos, add header blocks as needed. --- storage/formats/__init__.py | 8 ++++---- storage/formats/dmraid.py | 4 ++-- storage/formats/fs.py | 6 +++--- storage/formats/luks.py | 10 +++++----- storage/formats/lvmpv.py | 10 +++++----- storage/formats/mdraid.py | 10 +++++----- storage/formats/swap.py | 12 ++++++------ 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py index 016b3eb73..3abd77664 100644 --- a/storage/formats/__init__.py +++ b/storage/formats/__init__.py @@ -22,9 +22,9 @@ import os -from errors import * from iutil import notify_kernel, get_sysfs_path_by_name, log_method_call -from dm import dm_node_from_name +from ..errors import * +from ..devicelibs.dm import dm_node_from_name import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -55,7 +55,8 @@ def get_default_filesystem_type(boot=None): supported = get_device_format_class(fstype).supported except AttributeError: supported = None - elif supported: + + if supported: return fstype raise DeviceFormatError("None of %s is supported by your kernel" % ",".join(fstypes)) @@ -201,7 +202,6 @@ class DeviceFormat(object): else: name = os.path.basename(self.device) - path = get_sysfs_path_by_name(name) try: notify_kernel(path, action="change") diff --git a/storage/formats/dmraid.py b/storage/formats/dmraid.py index acf78da64..3e1d5c3e4 100644 --- a/storage/formats/dmraid.py +++ b/storage/formats/dmraid.py @@ -22,8 +22,8 @@ from iutil import log_method_call #from dm import dm_node_from_name -from errors import * -from deviceformat import * +from ..errors import * +from . import DeviceFormat, register_device_format import gettext _ = lambda x: gettext.ldgettext("anaconda", x) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index dcd340b59..c5ec44a7e 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -29,8 +29,8 @@ import os import isys -from errors import * -from deviceformat import * +from ..errors import * +from . import DeviceFormat, register_device_format import iutil import logging @@ -396,7 +396,7 @@ class FS(DeviceFormat): # passed in options override default options if not options or not isinstance(options, str): - options = ",".join(self.defaultMountOptions)) + options = ",".join(self.defaultMountOptions) try: rc = isys.mount(self.device, mountpoint, diff --git a/storage/formats/luks.py b/storage/formats/luks.py index d995fa68e..3485f56ad 100644 --- a/storage/formats/luks.py +++ b/storage/formats/luks.py @@ -24,10 +24,10 @@ import os -from errors import * from iutil import log_method_call -from deviceformat import * -import crypto +from ..errors import * +from ..devicelibs import crypto +from . import DeviceFormat, register_device_format import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -39,7 +39,7 @@ log = logging.getLogger("storage") class LUKS(DeviceFormat): """ A LUKS device. """ _type = "luks" - _name "LUKS" + _name = "LUKS" _udevTypes = ["crypto_LUKS"] _formattable = True # can be formatted _supported = True # is supported @@ -70,7 +70,7 @@ class LUKS(DeviceFormat): self.__passphrase = kwargs.get("passphrase") self._key_file = kwargs.get("key_file") - if not self.mapName: + if not self.mapName and self.device: self.mapName = "luks-%s" % os.path.basename(self.device) def _setPassphrase(self, passphrase): diff --git a/storage/formats/lvmpv.py b/storage/formats/lvmpv.py index 9747eb1b6..ac404f303 100644 --- a/storage/formats/lvmpv.py +++ b/storage/formats/lvmpv.py @@ -20,11 +20,11 @@ # Red Hat Author(s): Dave Lehman # -from errors import * from iutil import log_method_call -from deviceformat import * -import lvm -#from parted import PARTITION_LVM +from parted import PARTITION_LVM +from ..errors import * +from ..devicelibs import lvm +from . import DeviceFormat, register_device_format import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -38,7 +38,7 @@ class LVMPhysicalVolume(DeviceFormat): _type = "lvmpv" _name = "physical volume (LVM)" _udevTypes = ["LVM2_member"] - #partedFlags = PARTITION_LVM + partedFlags = PARTITION_LVM _formattable = True # can be formatted _supported = True # is supported _linuxNative = True # for clearpart diff --git a/storage/formats/mdraid.py b/storage/formats/mdraid.py index 0975269b1..90a631c18 100644 --- a/storage/formats/mdraid.py +++ b/storage/formats/mdraid.py @@ -22,11 +22,11 @@ import os -from errors import * from iutil import log_method_call -from deviceformat import * -import mdraid -#from parted import PARTITION_RAID +from parted import PARTITION_RAID +from ..errors import * +from ..devicelibs import mdraid +from . import DeviceFormat, register_device_format import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -40,7 +40,7 @@ class MDRaidMember(DeviceFormat): _type = "mdmember" _name = "software RAID" _udevTypes = ["linux_raid_member"] - #partedFlags = PARTITION_RAID + partedFlags = PARTITION_RAID _formattable = True # can be formatted _supported = True # is supported _linuxNative = True # for clearpart diff --git a/storage/formats/swap.py b/storage/formats/swap.py index 763921503..1888b9837 100644 --- a/storage/formats/swap.py +++ b/storage/formats/swap.py @@ -22,11 +22,11 @@ import os -from errors import * from iutil import log_method_call -from deviceformat import * -import swap -#from parted import PARTITION_SWAP +from parted import PARTITION_SWAP +from ..errors import * +from ..devicelibs import swap +from . import DeviceFormat, register_device_format import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -39,7 +39,7 @@ class SwapSpace(DeviceFormat): """ Swap space """ _type = "swap" _udevTypes = ["swap"] - #partedFlags = PARTITION_SWAP + partedFlags = PARTITION_SWAP _formattable = True # can be formatted _supported = True # is supported _linuxNative = True # for clearpart @@ -88,7 +88,7 @@ class SwapSpace(DeviceFormat): def _setOptions(self, opts): for (opt, arg) in opts.split(","): if opt == "pri": - try: + self.priority = numeric_type(arg) options = property(_getOptions, _setOptions, doc="The swap device's fstab options string") -- cgit From c0670ed695355563708c81557ba4e429f94e0f8b Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 18:12:10 -0600 Subject: Clean up imports, add header blocks as needed. --- storage/devicelibs/crypto.py | 2 +- storage/devicelibs/dm.py | 23 ++++++++++++++++++++++- storage/devicelibs/lvm.py | 23 ++++++++++++++++++++++- storage/devicelibs/mdraid.py | 23 ++++++++++++++++++++++- storage/devicelibs/swap.py | 5 ++++- 5 files changed, 71 insertions(+), 5 deletions(-) diff --git a/storage/devicelibs/crypto.py b/storage/devicelibs/crypto.py index c95b82493..0872ed052 100644 --- a/storage/devicelibs/crypto.py +++ b/storage/devicelibs/crypto.py @@ -22,7 +22,7 @@ import os import iutil -from errors import * +from ..errors import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) diff --git a/storage/devicelibs/dm.py b/storage/devicelibs/dm.py index adf8cee7f..02c88f614 100644 --- a/storage/devicelibs/dm.py +++ b/storage/devicelibs/dm.py @@ -1,8 +1,29 @@ +# +# dm.py +# device-mapper functions +# +# Copyright (C) 2009 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Author(s): Dave Lehman +# import os import iutil -from errors import * +from ..errors import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py index af1a9b7e5..14a11577f 100644 --- a/storage/devicelibs/lvm.py +++ b/storage/devicelibs/lvm.py @@ -1,10 +1,31 @@ +# +# lvm.py +# lvm functions +# +# Copyright (C) 2009 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Author(s): Dave Lehman +# import os import math import iutil -from errors import * +from ..errors import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) diff --git a/storage/devicelibs/mdraid.py b/storage/devicelibs/mdraid.py index 28e0a3510..75613d46a 100644 --- a/storage/devicelibs/mdraid.py +++ b/storage/devicelibs/mdraid.py @@ -1,8 +1,29 @@ +# +# mdraid.py +# mdraid functions +# +# Copyright (C) 2009 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Author(s): Dave Lehman +# import os import iutil -from errors import * +from ..errors import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) diff --git a/storage/devicelibs/swap.py b/storage/devicelibs/swap.py index 7d25bb3ed..b6c9b1ab8 100644 --- a/storage/devicelibs/swap.py +++ b/storage/devicelibs/swap.py @@ -19,11 +19,14 @@ # # Red Hat Author(s): Dave Lehman # + import iutil + +from ..errors import * + import gettext _ = lambda x: gettext.ldgettext("anaconda", x) -from errors import * def mkswap(device, label=''): argv = [] -- cgit From 3f35f8b87514a6946588f4f572fbf08c7de36e9c Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 18:17:24 -0600 Subject: Clean up imports, fix some minor errors, remove old test code. --- storage/devices.py | 24 ++++++-------- storage/devicetree.py | 88 ++++--------------------------------------------- storage/partitioning.py | 6 +--- storage/udev.py | 1 + 4 files changed, 18 insertions(+), 101 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index d0cc8431b..b62213cfe 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -96,22 +96,16 @@ import os import math # device backend modules -import mdraid -import lvm +import devicelibs.mdraid +import devicelibs.lvm #import block -import dm - -# XXX temporary -import sys -sys.path.insert(0, "/root/pyparted/src") -sys.path.insert(1, "/root/pyparted/src/.libs") -sys.path.append("devicelibs") +import devicelibs.dm import parted from errors import * from iutil import log_method_call, notify_kernel, numeric_type from udev import udev_settle -from deviceformat import get_device_format_class, getFormat +from formats import get_device_format_class, getFormat import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -365,7 +359,7 @@ class StorageDevice(Device): Device.__init__(self, device, parents=parents) self.uuid = None - self.format = None + self._format = getFormat(None) self._size = numeric_type(size) self.major = numeric_type(major) self.minor = numeric_type(minor) @@ -621,7 +615,7 @@ class DiskDevice(StorageDevice): def destroy(self): """ Destroy the device. """ log_method_call(self, self.name, status=self.status) - if self.status + if self.status: self.format.destroy() self.partedDisk.deleteAllPartitions() @@ -1037,8 +1031,7 @@ class DMDevice(StorageDevice): return dm.dm_node_from_name(self.name) #return block.getDmNodeFromName(self.name) - @name.setter - def setName(self, name): + def _setName(self, name): """ Set the device's map name. """ log_method_call(self, self.name, status=self.status) if self.status: @@ -1047,6 +1040,9 @@ class DMDevice(StorageDevice): self._name = name #self.sysfsPath = "/dev/disk/by-id/dm-name-%s" % self.name + name = property(lambda d: d._name, + lambda d,n: d._setName(n)) + def teardown(self, recursive=None): """ Close, or tear down, a device. """ log_method_call(self, self.name, status=self.status) diff --git a/storage/devicetree.py b/storage/devicetree.py index da1a60f71..7950d31f5 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -22,16 +22,10 @@ import os -import sys -sys.path.append("formats") - -if __name__ == "__main__": - import storage_log - from errors import * from devices import * from deviceaction import * -from deviceformat import getFormat +from formats import * from udev import * import gettext @@ -326,7 +320,7 @@ class DeviceTree(object): _action.obj == action.obj: #raise DeviceTreeError("duplicate action for this device") log.debug("cancelling action '%s' in favor of '%s'" % (_action, - action) + action)) self.cancelAction(_action) removedAction = _action break @@ -888,14 +882,16 @@ class DeviceTree(object): uuid = dev.uuid except AttributeError: uuid = None - elif uuid: + + if uuid: uuids[uuid] = dev try: uuid = dev.format.uuid except AttributeError: uuid = None - elif uuid: + + if uuid: uuids[uuid] = dev return uuids @@ -924,76 +920,4 @@ class DeviceTree(object): return [c for c in self._devices if device in c.parents] -def test_tree(): - try: - tree = DeviceTree(ignored_disks=[]) - except Exception as e: - log.error("tree creation failed: %s" % e) - raise - return tree - -def test_fstab(tree): - roots = findExistingRoots(tree, keepall=True) - print ["%s: %s" % (d.path, d.format.type) for d in roots] - rootDev = roots[0] - if not rootDev: - return - - log.debug("setting up root device %s" % rootDev.path) - #rootDev.setup() - #rootDev.format.mount(chroot="/mnt/sysimage", mountpoint="/") - #fstab = FSTab(tree, chroot="/mnt/sysimage") - fsset = FSSet(tree) - m - #return fstab - -if __name__ == "__main__": - mode = "tree" - if len(sys.argv) == 2: - mode = sys.argv[1] - - if mode == "tree": - tree = test_tree() - if tree is None: - sys.exit(1) - devices = tree.devices.values() - devices.sort(key=lambda d: d.path) - for device in devices: - fs_string = "" - if device.format: - fs_string = "%s on " % device.format.type - print "%s: %s%s" % (device.path, fs_string, device.typeDescription) - elif mode == "fstab": - tree = test_tree() - tree.teardownAll() - fstab = test_fstab(tree) - #print fstab.blkidTab.devices - #print fstab.cryptTab.mappings - fmt = "%-23s %-23s %-7s %-15s %s %s" - for in fstab.devices: - (device, mountpoint, fstype, options, dump, passno) = entry - print fmt % (device.fstabSpec(), mountpoint, fstype, - options, dump, passno) - - print - print "ORIGINAL:" - print fstab.origBuf - print - elif mode == "actions": - print "creating tree..." - tree = test_tree() - - # we don't need to actually use any of the devices, so clean up now - tree.teardownAll() - print "setting up actions..." - tree.registerAction(ActionDestroyFormat(tree.getDeviceByName("luks-md0"))) - tree.registerAction(ActionDestroyDevice(tree.getDeviceByName("luks-md0"))) - fs = getFormat("ext3", device="/dev/md0", mountpoint="/opt") - tree.registerAction(ActionCreateFormat(tree.getDeviceByName("md0"), fs)) - tree.registerAction(ActionDestroyFormat(tree.getDeviceByName("sda1"))) - print "processing actions..." - tree.processActions(dryRun=True) - print "done." - - tree.teardownAll() diff --git a/storage/partitioning.py b/storage/partitioning.py index ac7e5da53..ee78985cb 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -24,15 +24,11 @@ import os import copy from operator import add, sub -# XXX temporary -import sys -sys.path.insert(0, "/root/pyparted/src") -sys.path.insert(1, "/root/pyparted/src/.libs") import parted +from pykickstart.constants import * from errors import * from deviceaction import * -from pykickstart.constants import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) diff --git a/storage/udev.py b/storage/udev.py index cfc7837e6..05fa65c9b 100644 --- a/storage/udev.py +++ b/storage/udev.py @@ -24,6 +24,7 @@ import os import re import iutil +from errors import * import logging log = logging.getLogger("storage") -- cgit From 7b9c4ba09007965095f7542a341f6b20d219b36c Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 23:48:14 -0600 Subject: Remove a bunch of unused/obsolete stuff. --- autopart.py | 1596 ----------------------------------------------------------- 1 file changed, 1596 deletions(-) diff --git a/autopart.py b/autopart.py index 995bf9775..c447b3db3 100644 --- a/autopart.py +++ b/autopart.py @@ -51,20 +51,6 @@ BOOTALPHA_NOT_BSD = -3 BOOTALPHA_NO_RESERVED_SPACE = -4 BOOTIPSERIES_TOO_HIGH = -5 -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 ("anaconda.lvm", minLevel=logging.DEBUG) -lvmLog = logging.getLogger("anaconda.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(req, diskset): @@ -133,60 +119,6 @@ def bootAlphaCheckRequirements(part): return PARTITION_SUCCESS -def printFreespaceitem(part): - return part.getDeviceNodeName(), part.geometry.start, part.geometry.end, part.getSize(unit="MB") - -def printFreespace(free): - print("Free Space Summary:") - for drive in free.keys(): - print("On drive ", drive) - for part in free[drive]: - print("Freespace:", printFreespaceitem(part)) - - -def findFreespace(diskset): - free = {} - for drive in diskset.disks.keys(): - disk = diskset.disks[drive] - free[drive] = disk.getFreeSpacePartitions() - return free - - -def bestPartType(disk, request): - numPrimary = len(disk.getPrimaryPartitions()) - maxPrimary = disk.maxPrimaryPartitionCount - if numPrimary == maxPrimary: - raise PartitioningError, "Unable to create additional primary partitions on /dev/%s" % (disk.device.path[5:]) - if request.primary: - return parted.PARTITION_NORMAL - if ((numPrimary == (maxPrimary - 1)) and - not disk.getExtendedPartition() and - disk.supportsFeature(parted.DISK_TYPE_EXTENDED)): - return parted.PARTITION_EXTENDED - return parted.PARTITION_NORMAL - -class partlist: - def __init__(self): - self.parts = [] - - def __str__(self): - retval = "" - for p in self.parts: - retval = retval + "\t%s %s %s\n" % (p.getDeviceNodeName(), partedUtils.get_partition_file_system_type(p), p.getSize(unit="MB")) - - return retval - - def reset(self): - dellist = [] - for part in self.parts: - dellist.append(part) - - for part in dellist: - self.parts.remove(part) - del part - - self.parts = [] - def getMinimumSector(disk): if disk.type == 'sun': (cylinders, heads, sectors) = disk.device.biosGeometry @@ -195,1514 +127,6 @@ def getMinimumSector(disk): return start + 1 return 0L -# first step of partitioning voodoo -# partitions with a specific start and end cylinder requested are -# placed where they were asked to go -def fitConstrained(diskset, requests, primOnly=0, newParts = None): - for request in requests.requests: - if request.type != REQUEST_NEW: - continue - if request.device: - continue - if primOnly and not request.primary and not requests.isBootable(request): - continue - if request.drive and (request.start != None): - if not request.end and not request.size: - raise PartitioningError, "Tried to create constrained partition without size or end" - - fsType = request.fstype.getPartedFileSystemType() - disk = diskset.disks[request.drive[0]] - if not disk: # this shouldn't happen - raise PartitioningError, "Selected to put partition on non-existent disk!" - - startSec = disk.device.startCylinderToSector(request.start) - - if request.end: - endCyl = request.end - elif request.size: - endCyl = disk.device.endSectorToCylinder(((1024L * 1024L * request.size) / disk.device.sectorSize) + startSec) - - endSec = disk.device.endCylinderToSector(endCyl) - - if endSec > disk.device.length: - raise PartitioningError, "Unable to create partition which extends beyond the end of the disk." - - # XXX need to check overlaps properly here - minSec = getMinimumSector(disk) - if startSec < minSec: - startSec = minSec - - if disk.supportsFeature(parted.DISK_TYPE_EXTENDED) and disk.getExtendedPartition(): - extpart = disk.getExtendedPartition() - if (extpart.geometry.start < startSec) and (extpart.geometry.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(disk.getLogicalPartitions()) == - disk.getMaxLogicalPartitions()): - raise PartitioningError, "Cannot create another logical partition for %s." % request.mountpoint - else: - partType = parted.PARTITION_NORMAL - else: - # XXX need a better way to do primary vs logical stuff - ret = bestPartType(disk, request) - - if ret == parted.PARTITION_NORMAL: - partType = parted.PARTITION_NORMAL - elif ret == parted.PARTITION_EXTENDED: - geometry = parted.Geometry(device=disk.device, - start=startSec, - end=endSec) - newp = parted.Partition(disk=disk, - type=parted.PARTITION_EXTENDED, - geometry=geometry) - constraint = parted.Constraint(device=disk.device) - disk.addPartition(newp, constraint) - disk.maximizePartition(newp, constraint) - newParts.parts.append(newp) - requests.nextUniqueID = requests.nextUniqueID + 1 - partType = parted.PARTITION_LOGICAL - else: # shouldn't get here - raise PartitioningError, "Impossible partition type to create" - geometry = parted.Geometry(device=disk.device, - start=startSec, - end=endSec) - newp = parted.Partition(disk=disk, type=partType, - fs=fsType, geometry=geometry) - constraint = parted.Constraint(device=disk.device) - try: - disk.addPartition(newp, constraint) - - except Exception, msg: - raise PartitioningError, str(msg) - for flag in request.fstype.getPartedPartitionFlags(): - if not newp.isFlagAvailable(flag): - disk.deletePartition(newp) - raise PartitioningError, ("requested FileSystemType needs " - "a flag that is not available.") - newp.setFlag(flag) - request.device = fsset.PartedPartitionDevice(newp).getDevice() - request.currentDrive = request.drive[0] - newParts.parts.append(newp) - -# get the list of the "best" drives to try to use... -# if currentdrive is set, use that, else use the drive list, or use -# all the drives -def getDriveList(request, diskset): - if request.currentDrive: - drives = request.currentDrive - elif request.drive: - drives = request.drive - else: - drives = diskset.disks.keys() - - if not type(drives) == type([]): - drives = [ drives ] - - drives.sort(isys.compareDrives) - - return drives - - -# fit partitions of a specific size with or without a specific disk -# into the freespace -def fitSized(diskset, requests, primOnly = 0, newParts = None): - todo = {} - - for request in requests.requests: - if request.type != REQUEST_NEW: - continue - if request.device: - continue - if primOnly and not request.primary and not requests.isBootable(request): - continue - if request.size == 0 and request.requestSize == 0: - request.requestSize = 1 - 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) - if not todo.has_key(numDrives): - todo[numDrives] = [ request ] - else: - todo[numDrives].append(request) - - number = todo.keys() - number.sort() - free = findFreespace(diskset) - - for num in number: - for request in todo[num]: -# print("\nInserting ->", request) - if requests.isBootable(request): - isBoot = 1 - else: - isBoot = 0 - - largestPart = (0, None) - drives = getDriveList(request, diskset) - 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) - disk = diskset.disks[drive] - numPrimary = len(disk.getPrimaryPartitions()) - numLogical = len(disk.getLogicalPartitions()) - - # if there is an extended partition add it in - if disk.getExtendedPartition(): - numPrimary = numPrimary + 1 - - maxPrimary = disk.maxPrimaryPartitionCount - maxLogical = disk.getMaxLogicalPartitions() - - for part in free[drive]: - # if this is a free space outside extended partition - # make sure we have free primary partition slots - if not part.type & parted.PARTITION_LOGICAL: - if numPrimary == maxPrimary: - continue - else: - if numLogical == maxLogical: - continue - - lvmLog.debug( "Trying partition %s" % (printFreespaceitem(part),)) - partSize = part.getSize(unit="MB") - # figure out what the request size will be given the - # geometry (#130885) - requestSectors = long((request.requestSize * 1024L * 1024L) / part.disk.device.sectorSize) - 1 - requestSizeMB = long((requestSectors * part.disk.device.sectorSize) / 1024L / 1024L) - 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) - - lvmLog.debug("largestPart is %s" % (largestPart,)) - freespace = largestPart[1] - freeStartSec = freespace.geometry.start - freeEndSec = freespace.geometry.end - - dev = freespace.geometry.device - disk = freespace.disk - - startSec = freeStartSec - - # For alpha reserve space at the begining of disk - if iutil.isAlpha() and startSec < long((1024L * 1024L)/disk.device.sectorSize): - startSec = long((2 * 1024L * 1024L)/disk.device.sectorSize) - - endSec = startSec + long(((request.requestSize * 1024L * 1024L) / disk.device.sectorSize)) - 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 ret == parted.PARTITION_NORMAL: - partType = parted.PARTITION_NORMAL - elif ret == parted.PARTITION_EXTENDED: - geometry = parted.Geometry(device=disk.device, - start=startSec, - end=endSec) - newp = parted.Partition(disk=disk, - type=parted.PARTITION_EXTENDED, - geometry=geometry) - constraint = parted.Constraint(device=disk.device) - disk.addPartition(newp, constraint) - disk.maximizePartition(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 - for part in disk.getFreeSpacePartitions(): - if part.geometry.start > freeStartSec and part.geometry.end <= freeEndSec: - found = 1 - freeStartSec = part.geometry.start - freeEndSec = part.geometry.end - break - - 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.device.sectorSize)) - 1 - - if endSec > freeEndSec: - endSec = freeEndSec - if startSec < freeStartSec: - startSec = freeStartSec - - 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)) - geometry = parted.Geometry(device=disk.device, - start=startSec, - end=endSec) - fs=parted.FileSystem(type=fsType.name, geometry=geometry) - newp = parted.Partition(disk=disk, type=partType, - fs=fs, geometry=geometry) - constraint = parted.Constraint(device=disk.device) - - try: - disk.addPartition(newp, constraint) - except Exception, msg: - raise PartitioningError, str(msg) - for flag in request.fstype.getPartedPartitionFlags(): - if not newp.isFlagAvailable(flag): - disk.deletePartition(newp) - raise PartitioningError, ("requested FileSystemType needs " - "a flag that is not available.") - newp.setFlag(flag) - - request.device = fsset.PartedPartitionDevice(newp).getDevice() - drive = newp.geometry.device.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): - - # returns free space segments for each drive IN SECTORS - def getFreeSpace(diskset): - free = findFreespace(diskset) - freeSize = {} - largestFree = {} - - # find out the amount of free space on each drive - for key in free.keys(): - if len(free[key]) == 0: - del free[key] - continue - freeSize[key] = 0 - largestFree[key] = 0 - for part in free[key]: - sz = part.geometry.length - freeSize[key] += sz - if sz > largestFree[key]: - largestFree[key] = sz - - return (free, freeSize, largestFree) - - #### - # start of growParts - #### - newRequest = requests.copy() - - (free, freeSize, largestFree) = getFreeSpace(diskset) - - # find growable partitions - growable = {} - growSize = {} - origSize = {} - for request in newRequest.requests: - if request.type != REQUEST_NEW or not request.grow: - continue - - origSize[request.uniqueID] = request.requestSize - if not growable.has_key(request.currentDrive): - growable[request.currentDrive] = [ request ] - else: - growable[request.currentDrive].append(request) - - # there aren't any drives with growable partitions, this is easy! - if not growable.keys(): - return - - # loop over all drives, grow all growable partitions one at a time - grownList = [] - for drive in growable.keys(): - # no free space on this drive, so can't grow any of its parts - if not free.has_key(drive): - continue - - # process each request - # grow all growable partitions on this drive until all can grow no more - donegrowing = 0 - 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 drive not in freeSize.keys(): -# 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(diskset.diskState()) - - - outer_iter = outer_iter + 1 - donegrowing = 1 - - # pull out list of requests we want to grow on this drive - growList = growable[drive] - - sectorSize = diskset.disks[drive].device.sectorSize - (cylinders, heads, sectors) = diskset.disks[drive].device.biosGeometry - cylsectors = sectors * heads - - # sort in order of request size, consider biggest first - n = 0 - while n < len(growList): - for request in growList: - if request.size < growList[n].size: - tmp = growList[n] - index = growList.index(request) - growList[n] = request - growList[index] = tmp - n = n + 1 - - # recalculate the total size of growable requests for this drive - # NOTE - we add up the ORIGINAL requested sizes, not grown sizes - growSize[drive] = 0 - for request in growList: - if request.uniqueID in grownList: - continue - growSize[drive] = growSize[drive] + origSize[request.uniqueID] - - thisFreeSize = getFreeSpace(diskset)[1] - # loop over requests for this drive - for request in growList: - # skip if we've finished growing this request - if request.uniqueID in grownList: - continue - - if drive not in freeSize.keys(): - donegrowing = 1 -# 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]) - - donegrowing = 0 - - # get amount of space actually used by current allocation - startSize = 0 - - for drive in request.drive: - part = diskset.disks[drive].getPartitionByPath("/dev/%s" % request.device) - if part: - startSize += part.geometry.length - - # compute fraction of freespace which to give to this - # request. Weight by original request size - percent = origSize[request.uniqueID] / (growSize[drive] * 1.0) - growby = long(percent * thisFreeSize[drive]) - if 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) - - imposedMax = 0 - if request.maxSizeMB: - # round down a cylinder, see comment below - tmpint = request.maxSizeMB*1024.0*1024.0/sectorSize - tmpint = long(tmpint / cylsectors) - maxUserSize = tmpint * cylsectors - 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) = iutil.swapSuggestion(quiet=1) - - # convert to sectors - tmpint = tmpint*1024*1024/sectorSize - tmpint = long(tmpint / cylsectors) - maxsugswap = tmpint * cylsectors - userstartsize = origSize[request.uniqueID]*1024*1024/sectorSize - if maxsugswap >= userstartsize: - maxsect = maxsugswap - 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/sectorSize - 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] * sectorSize/(1024.0 * 1024.0), maxsect * sectorSize/(1024.0*1024.0), largestFree[drive] * sectorSize/(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): - # XXX need to request in sectors preferably, more accurate -## print("trying cur=%s" % cur) - request.requestSize = (cur*sectorSize)/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*sectorSize/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 - - disks = set() - for drive in request.drive: - if not diskset.disks.has_key(drive): - lvmLog.info("pre-existing partition on non-native disk %s, ignoring" %(drive,)) - continue - disks.add(diskset.disks[drive]) - - parts = set() - for disk in list(disks): - for part in disk.partitions: - parts.add(part) - - for part in list(parts): - if part.geometry.start == request.start and part.geometry.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.geometry.start - endSec = part.geometry.start + long(((request.targetSize * 1024L * 1024L) / disk.device.sectorSize)) - 1 - - try: - g = copy.deepcopy(part.geometry) - g.end = endSec - constraint = parted.Constraint(exactGeom=g) - part.geometry = g - except Exception, msg: - log.error("error setting geometry for partition %s: %s" %(part.getDeviceNodeName(), msg)) - raise PartitioningError, _("Error resizing partition %s.\n\n%s") %(part.getDeviceNodeName(), msg) - - if startSec != part.geometry.start: - raise PartitioningError, _("Start of partition %s was moved when resizing") %(part.getDeviceNodeName(),) - - request.device = part.getDeviceNodeName() - if request.fstype: - if request.fstype.getName() != request.origfstype.getName(): - if part.isFlagAvailable(parted.PARTITION_RAID): - if request.fstype.getName() == "software RAID": - part.setFlag(parted.PARTITION_RAID) - else: - part.unsetFlag(parted.PARTITION_RAID) - if part.isFlagAvailable(parted.PARTITION_LVM): - if request.fstype.getName() == "physical volume (LVM)": - part.setFlag(parted.PARTITION_LVM) - else: - part.unsetFlag(parted.PARTITION_LVM) - - partedUtils.set_partition_file_system_type(part, request.fstype) - - break - -def deletePart(diskset, delete): - disk = diskset.disks[delete.drive] - for part in disk.partitions: - if part.geometry.start == delete.start and part.geometry.end == delete.end: - disk.deletePartition(part) - return - -def processPartitioning(diskset, requests, newParts): - # collect a hash of all the devices that we have created extended - # partitions on. When we remove these extended partitions the logicals - # (all of which we created) will be destroyed along with it. - extendeds = {} - - for part in newParts.parts: - if part.type == parted.PARTITION_EXTENDED: - extendeds[part.geometry.device.path] = None - - # Go through the list again and check for each logical partition we have. - # If we created the extended partition on the same device as the logical - # partition, remove it from out list, as it will be cleaned up for us - # when the extended partition gets removed. - dellist = [] - for part in newParts.parts: - if (part.type & parted.PARTITION_LOGICAL - and extendeds.has_key(part.geometry.device.path)): - dellist.append(part) - - for part in dellist: - newParts.parts.remove(part) - - # Finally, remove all of the partitions we added in the last try from - # the disks. We'll start again from there. - for part in newParts.parts: - part.disk.deletePartition(part) - - newParts.reset() - - for request in requests.requests: - if request.type == REQUEST_NEW: - request.device = None - - setPreexistParts(diskset, requests.requests) - - # sort requests by size - requests.sortRequests() - - # partitioning algorithm in simplistic terms - # - # we want to allocate partitions such that the most specifically - # spelled out partitions get what they want first in order to ensure - # they don't get preempted. first conflict found returns an error - # which must be handled by the caller by saying that the partition - # add is impossible (XXX can we get an impossible situation after delete?) - # - # potentially confusing terms - # type == primary vs logical - # - # order to allocate: - # start and end cylinders given (note that start + size & !grow is equivalent) - # drive, partnum - # drive, type - # drive - # priority partition (/boot or /) - # size - - # run through with primary only constraints first - try: - fitConstrained(diskset, requests, 1, newParts) - except PartitioningError, 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") + str(msg) - - try: - fitConstrained(diskset, requests, 0, newParts) - except PartitioningError, 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 - - # 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 - if request.maxResizeSize is None: - request.maxResizeSize = 0 - - for drive in request.drive: - part = diskset.disks[drive].getPartitionByPath("/dev/%s" % request.device) - if part: - request.maxResizeSize += part.getMaxAvailableSize(unit="MB") - -## print("disk layout after everything is done") -## print(diskset.diskState()) - -def doPartitioning(diskset, requests, doRefresh = 1): - for request in requests.requests: - request.requestSize = request.size - request.currentDrive = None - - if doRefresh: - diskset.refreshDevices() - # XXX - handle delete requests - for delete in requests.deletes: - if isinstance(delete, partRequests.DeleteSpec): - deletePart(diskset, delete) - # FIXME: do we need to do anything with other types of deletes?? - - newParts = partlist() - - try: - processPartitioning(diskset, requests, newParts) - except PartitioningError, msg: - raise PartitioningError, "Partitioning failed: %s" % msg - - growParts(diskset, requests, newParts) - - newParts.reset() - - 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 -# this the first time -def doClearPartAction(anaconda, partitions, diskset): - type = partitions.autoClearPartType - cleardrives = partitions.autoClearPartDrives - initAll = partitions.reinitializeDisks - - if type == CLEARPART_TYPE_LINUX: - linuxOnly = 1 - elif type == CLEARPART_TYPE_ALL: - linuxOnly = 0 - elif type == CLEARPART_TYPE_NONE: - return - else: - raise ValueError, "Invalid clear part type in doClearPartAction" - - drives = diskset.disks.keys() - drives.sort() - - for drive in drives: - # skip drives not in clear drive list - if (cleardrives and len(cleardrives) > 0 and not drive in cleardrives) or \ - drive in diskset.skippedDisks: - continue - disk = diskset.disks[drive] - for part in disk.partitions: - if (not part.active or (part.type == parted.PARTITION_EXTENDED) or - (part.disk.type == "mac" and part.number == 1 and part.name == "Apple")): - continue - if part.fileSystem: - ptype = partedUtils.get_partition_file_system_type(part) - else: - ptype = None - # we want to do the clearing if - # 1) clearAll is set - # 2) there's a fsystem on the partition and it's a "native" fs - # 3) there's not fsystem but the numeric id of partition is native - # 4) the ptable doesn't support numeric ids, but it appears to be - # 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, anaconda)) or - (not ptype and - partedUtils.isLinuxNative(part)) or - ((part.fileSystem is None) and # the ptable doesn't have types - ((part.isFlagAvailable(parted.PARTITION_RAID) and part.getFlag(parted.PARTITION_RAID)) or # this is a RAID - (part.isFlagAvailable(parted.PARTITION_LVM) and part.getFlag(parted.PARTITION_LVM)) or # or an LVM - (iutil.isMactel() and not ptype)))): # or we're on a mactel and have a blank partition from bootcamp #FIXME: this could be dangerous... - old = partitions.getRequestByDeviceName(part.getDeviceNodeName()) - if old.getProtected(): - continue - - partitions.deleteDependentRequests(old) - partitions.removeRequest(old) - - drive = partedUtils.get_partition_drive(part) - delete = partRequests.DeleteSpec(drive, part.geometry.start, - part.geometry.end) - partitions.addDelete(delete) - - # EFI autopartitioning is strange as /boot/efi is efi (which is - # really vfat) -- - # if linuxonly and have an msdos partition and it has the - # bootable flag set, do not delete it and make it our - # /boot/efi as it could contain system utils. - # doesn't apply on kickstart installs or if no boot flag - if iutil.isEfi() and linuxOnly == 1 and (not anaconda.isKickstart): - if partedUtils.isEfiSystemPartition(part): - req = partitions.getRequestByDeviceName(part.getDeviceNodeName()) - other = partitions.getRequestByMountPoint("/boot/efi") - - if not other: - req.mountpoint = "/boot/efi" - req.format = 0 - req.fstype = fsset.fileSystemTypeGet("efi") - - request = None - for req in partitions.autoPartitionRequests: - if req.mountpoint == "/boot/efi": - request = req - break - if request: - partitions.autoPartitionRequests.remove(request) - # hey, what do you know, pseries is weird too. *grumble* - elif (((iutil.getPPCMachine() == "pSeries") or - (iutil.getPPCMachine() == "iSeries")) - and (linuxOnly == 1) - and (not anaconda.isKickstart) and - part.isFlagAvailable(parted.PARTITION_BOOT) and - (part.getFlag(parted.PARTITION_PREP)) and - part.getFlag(parted.PARTITION_BOOT)): - req = partitions.getRequestByDeviceName(part.getDeviceNodeName()) - req.mountpoint = None - req.format = 0 - request = None - for req in partitions.autoPartitionRequests: - if req.fstype == fsset.fileSystemTypeGet("PPC PReP Boot"): - request = req - break - if request: - partitions.autoPartitionRequests.remove(request) - - # set the diskset up - try: - doPartitioning(diskset, partitions, doRefresh = 1) - except PartitioningError: # if we get an error here, it isn't overly relevant - pass - - for drive in drives: - if (cleardrives and len(cleardrives) > 0 and not drive in cleardrives) or \ - drive in diskset.skippedDisks: - continue - - disk = diskset.disks[drive] - ext = disk.getExtendedPartition() - # if the extended is empty, blow it away - if ext and len(disk.getLogicalPartitions()) == 0: - delete = partRequests.DeleteSpec(drive, ext.geometry.start, - ext.geometry.end) - old = partitions.getRequestByDeviceName(ext.getDeviceNodeName()) - partitions.removeRequest(old) - partitions.addDelete(delete) - deletePart(diskset, delete) - continue - -def doAutoPartition(anaconda): - instClass = anaconda.id.instClass - diskset = anaconda.id.diskset - partitions = anaconda.id.partitions - - if anaconda.isKickstart: - partitions.setProtected(anaconda.dispatch) - - if anaconda.dir == DISPATCH_BACK: - diskset.refreshDevices() - partitions.setFromDisk(diskset) - partitions.setProtected(anaconda.dispatch) - partitions.autoPartitionRequests = [] - return - - # if no auto partition info in instclass we bail - if len(partitions.autoPartitionRequests) < 1: - #return DISPATCH_NOOP - # XXX if we noop, then we fail later steps... let's just make it - # the workstation default. should instead just never get here - # if no autopart info - instClass.setDefaultPartitioning(partitions, doClear = 0) - - # reset drive and request info to original state - # XXX only do this if we're dirty -## id.diskset.refreshDevices() -## id.partrequests = PartitionRequests(id.diskset) - doClearPartAction(anaconda, partitions, diskset) - - # 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 filter (lambda d: d in initial_free_keys, partitions.autoClearPartDrives): - free = 0 - for f in initial_free[drive]: - size = f.geometry.end - f.geometry.start - # don't count any partition smaller than 1M - if (size > 2048): - free += size - for req in partitions.deletes: - if isinstance(req, partRequests.DeleteSpec) and (drive in req.drive): - size = req.end - req.start - # don't count any partition smaller than 1M - if (size > 2048): - free += size - - # If there's less than 10M free, forget it. - if free > 20480: - drives.append(drive) - del initial_free - - for request in partitions.autoPartitionRequests: - if (isinstance(request, partRequests.PartitionSpec) and - request.device): - # get the preexisting partition they want to use - req = partitions.getRequestByDeviceName(request.device) - if not req or not req.type or req.type != REQUEST_PREEXIST: - anaconda.intf.messageWindow(_("Requested Partition Does Not Exist"), - _("Unable to locate partition %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 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: - anaconda.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: - anaconda.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: - anaconda.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: - req.fsopts = request.fsopts - if not request.format: - req.format = 0 - else: - req.format = 1 - req.fstype = request.fstype - else: - req = copy.copy(request) - - 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: - req.drive = diskset.disks.keys() - - 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, anaconda.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: - anaconda.intf.messageWindow(_("Automatic Partitioning Errors"), - _("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: - if not anaconda.isKickstart: - anaconda.intf.messageWindow(_("Warnings During Automatic Partitioning"), - _("Following warnings occurred during automatic " - "partitioning:\n\n%s") % (msg,), - custom_icon='warning') - else: - lvmLog.warning(str(msg)) - except PartitioningError, msg: - # restore drives to original state - diskset.refreshDevices() - partitions.setFromDisk(diskset) - partitions.setProtected(anaconda.dispatch) - if not anaconda.isKickstart: - extra = "" - anaconda.dispatch.skipStep("partition", skip = 0) - else: - extra = _("\n\nPress 'OK' to exit the installer.") - anaconda.intf.messageWindow(_("Error Partitioning"), - _("Could not allocate requested partitions: \n\n" - "%s.%s") % (msg, extra), custom_icon='error') - - - if anaconda.isKickstart: - sys.exit(0) - - # now do a full check of the requests - (errors, warnings) = partitions.sanityCheckAllRequests(diskset) - if warnings: - for warning in warnings: - lvmLog.warning(warning) - if errors: - errortxt = string.join(errors, '\n') - if anaconda.isKickstart: - extra = _("\n\nPress 'OK' to exit the installer.") - else: - extra = _("\n\nPress 'OK' to choose a different partitioning option.") - - anaconda.intf.messageWindow(_("Automatic Partitioning Errors"), - _("The following errors occurred with your " - "partitioning:\n\n%s\n\n" - "This can happen if there is not enough " - "space on your hard drive(s) for the " - "installation. %s") - % (errortxt, extra), - custom_icon='error') - # - # XXX if in kickstart we reboot - # - if anaconda.isKickstart: - anaconda.intf.messageWindow(_("Unrecoverable Error"), - _("Your system will now be rebooted.")) - sys.exit(0) - return DISPATCH_BACK - -def autoCreatePartitionRequests(autoreq): - """Return a list of requests created with a shorthand notation. - - Mainly used by installclasses; make a list of tuples of the form - (mntpt, fstype, minsize, maxsize, grow, format, asvol) - 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 (ignored) - """ - - requests = [] - for (mntpt, fstype, minsize, maxsize, grow, format, asvol) in autoreq: - if fstype: - ptype = fsset.fileSystemTypeGet(fstype) - else: - ptype = fsset.fileSystemTypeGetDefault() - - newrequest = partRequests.PartitionSpec(ptype, - mountpoint = mntpt, - size = minsize, - maxSizeMB = maxsize, - grow = grow, - format = format) - - requests.append(newrequest) - - return requests - -def autoCreateLVMPartitionRequests(autoreq): - """Return a list of requests created with a shorthand notation using LVM. - - 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 - """ - - 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 - swapvol = 0 - totalswaps = 0 - - for (mntpt, fstype, minsize, maxsize, grow, format, asvol) in autoreq: - if fsset.fileSystemTypeGet(fstype) == fsset.fileSystemTypeGet("swap"): - totalswaps += 1 - - for (mntpt, fstype, minsize, maxsize, grow, format, asvol) in autoreq: - ptype = fsset.fileSystemTypeGet(fstype) - - if not asvol: - newrequest = partRequests.PartitionSpec(ptype, - mountpoint = mntpt, - size = minsize, - maxSizeMB = maxsize, - grow = grow, - format = format) - else: - # try to incorporate the mount point in to the logical volume name - if mntpt is not None and mntpt != '': - if mntpt == '/': - lvtemplate = 'lv_root' - else: - tmp = lvm.safeLvmName(mntpt) - lvtemplate = "lv_%s" % (tmp,) - else: - if ptype == fsset.fileSystemTypeGet("swap"): - if totalswaps > 1: - lvtemplate = "lv_swap%02d" % (swapvol,) - swapvol += 1 - else: - lvtemplate = "lv_swap" - else: - lvtemplate = "LogVol%02d" % (volnum,) - volnum += 1 - - newrequest = partRequests.LogicalVolumeRequestSpec(ptype, - mountpoint = mntpt, - size = minsize, - maxSizeMB = maxsize, - grow = grow, - format = format, - lvname = "%s" % (lvtemplate,), - volgroup = "lvm") - - requests.append(newrequest) - - return requests - def getAutopartitionBoot(partitions): """Return the proper shorthand for the boot dir (arch dependent).""" fsname = fsset.fileSystemTypeGetDefaultBoot().getName() @@ -1729,23 +153,3 @@ def getAutopartitionBoot(partitions): return [ ("/boot", fsname, 200, None, 0, 1, 0) ] -# 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.") - -AUTOPART_DISK_CHOICE_DESCR_TEXT = N_("Before automatic partitioning can be " - "set up by the installation program, you " - "must choose how to use the space on " - "your hard drives.") - -CLEARPART_TYPE_ALL_DESCR_TEXT = N_("Remove all partitions on this system") -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") -- cgit From 89d473cad8a864727b1d13b793c0a9cba1d5de5e Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 23:48:44 -0600 Subject: Remove obsolete import from autopart. --- dispatch.py | 1 - 1 file changed, 1 deletion(-) diff --git a/dispatch.py b/dispatch.py index a09a86183..7501bd145 100644 --- a/dispatch.py +++ b/dispatch.py @@ -27,7 +27,6 @@ from packages import writeKSConfiguration, turnOnFilesystems from packages import doMigrateFilesystems from packages import doPostAction from packages import copyAnacondaLogs -from autopart import doAutoPartition from packages import firstbootConfiguration from packages import betaNagScreen from packages import setupTimezone -- cgit From 27c7677da6a6f7b9db3a95ed7b7732c905fff730 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 23:50:46 -0600 Subject: Remove format flag from autorequest tuple since it's always True. --- installclass.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/installclass.py b/installclass.py index 4a1e95269..a028a0a06 100644 --- a/installclass.py +++ b/installclass.py @@ -188,15 +188,15 @@ class BaseInstallClass(object): return AnacondaBackend def setDefaultPartitioning(self, storage, clear = CLEARPART_TYPE_LINUX, - doClear = 1, useLVM = True): - autorequests = [ ("/", None, 1024, None, 1, 1, 1) ] + doClear = True, useLVM = True): + autorequests = [ ("/", None, 1024, None, True, True) ] bootreq = getAutopartitionBoot(storage) if bootreq: autorequests.extend(bootreq) (minswap, maxswap) = iutil.swapSuggestion() - autorequests.append((None, "swap", minswap, maxswap, 1, 1, 1)) + autorequests.append((None, "swap", minswap, maxswap, True, True)) if doClear: storage.autoClearPartType = clear -- cgit From a11a82100c1f40222c585d3d70b95e625de414df Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 23:51:40 -0600 Subject: Update to use new storage module. --- iw/autopart_type.py | 92 ++++++++++++++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 46 deletions(-) diff --git a/iw/autopart_type.py b/iw/autopart_type.py index 3db0c4e3b..8963a2c6d 100644 --- a/iw/autopart_type.py +++ b/iw/autopart_type.py @@ -23,7 +23,6 @@ import gtk import gobject import math -import autopart from constants import * import gui from partition_ui_helpers_gui import * @@ -32,26 +31,25 @@ from netconfig_dialog import NetworkConfigurator from iw_gui import * from flags import flags import network -import partitions -import iscsi +from storage import iscsi import gettext _ = lambda x: gettext.ldgettext("anaconda", x) -def whichToResize(partitions, diskset, intf): +def whichToResize(storage, intf): def getActive(combo): act = combo.get_active_iter() return combo.get_model().get_value(act, 1) def comboCB(combo, resizeSB): # partition to resize changed, let's update our spinbutton - req = getActive(combo) - if req.targetSize is not None: - value = req.targetSize + part = getActive(combo) + if part.targetSize is not None: + value = part.targetSize else: - value = req.size - reqlower = req.getMinimumResizeMB(partitions) - requpper = req.getMaximumResizeMB(partitions) + value = part.size + reqlower = part.minSize + requpper = part.maxSize adj = resizeSB.get_adjustment() adj.lower = reqlower @@ -72,16 +70,17 @@ def whichToResize(partitions, diskset, intf): found = False biggest = -1 - for req in partitions.requests: - if req.type != REQUEST_PREEXIST: + for part in storage.partitions: + if not part.exists: continue - if req.isResizable(partitions): + + if part.resizable: i = store.append(None) - store[i] = ("%s (%s, %d MB)" %(req.device, - req.fstype.getName(), - math.floor(req.size)), - req) - if req.targetSize is not None: + store[i] = ("%s (%s, %d MB)" %(part.name, + part.format.name, + math.floor(part.size)), + part) + if part.targetSize is not None: combo.set_active_iter(i) found = True else: @@ -119,7 +118,7 @@ class PartitionTypeWindow(InstallWindow): ics.setNextEnabled(True) def getNext(self): - if self.diskset.checkNoDisks(): + if self.storage.checkNoDisks(): raise gui.StayOnScreen active = self.combo.get_active_iter() @@ -131,7 +130,7 @@ class PartitionTypeWindow(InstallWindow): self.dispatch.skipStep("bootloader", skip = 0) else: if val == -2: - rc = whichToResize(self.partitions, self.diskset, self.intf) + rc = whichToResize(self.storage, self.intf) if rc != gtk.RESPONSE_OK: raise gui.StayOnScreen @@ -141,14 +140,14 @@ class PartitionTypeWindow(InstallWindow): self.dispatch.skipStep("autopartitionexecute", skip = 0) if self.xml.get_widget("encryptButton").get_active(): - self.partitions.autoEncrypt = True + self.storage.encryptedAutoPart = True else: - self.partitions.encryptionPassphrase = "" - self.partitions.retrofitPassphrase = False - self.partitions.autoEncrypt = False + self.storage.encryptionPassphrase = "" + self.storage.retrofitPassphrase = False + self.storage.encryptedAutoPart = False - self.partitions.useAutopartitioning = 1 - self.partitions.autoClearPartType = val + self.storage.doAutoPart = True + self.storage.clearPartType = val allowdrives = [] model = self.drivelist.get_model() @@ -160,7 +159,7 @@ class PartitionTypeWindow(InstallWindow): mustHaveSelectedDrive(self.intf) raise gui.StayOnScreen - self.partitions.autoClearPartDrives = allowdrives + self.storage.clearPartDrives = allowdrives # pop the boot device to be first in the drive list defiter = self.bootcombo.get_active_iter() @@ -239,8 +238,8 @@ class PartitionTypeWindow(InstallWindow): # get the initiator name if it exists and don't allow changing # once set e = dxml.get_widget("iscsiInitiatorEntry") - e.set_text(self.anaconda.id.iscsi.initiator) - if self.anaconda.id.iscsi.initiatorSet: # this is uglyyyy.... + e.set_text(self.storage.iscsi.initiator) + if self.storage.iscsi.initiatorSet: # this is uglyyyy.... e.set_sensitive(False) while 1: @@ -255,7 +254,7 @@ class PartitionTypeWindow(InstallWindow): _("You must provide an initiator name.")) continue - self.anaconda.id.iscsi.initiator = initiator + self.storage.iscsi.initiator = initiator target = dxml.get_widget("iscsiAddrEntry").get_text().strip() user = dxml.get_widget("userEntry").get_text().strip() @@ -291,8 +290,8 @@ class PartitionTypeWindow(InstallWindow): continue try: - self.anaconda.id.iscsi.addTarget(ip, port, user, pw, user_in, pw_in, - self.intf) + self.storage.iscsi.addTarget(ip, port, user, pw, + user_in, pw_in, self.intf) except ValueError, e: self.intf.messageWindow(_("Error"), str(e)) continue @@ -324,7 +323,7 @@ class PartitionTypeWindow(InstallWindow): fcplun = dxml.get_widget("fcplunEntry").get_text().strip() try: - self.anaconda.id.zfcp.addFCP(devnum, wwpn, fcplun) + self.storage.zfcp.addFCP(devnum, wwpn, fcplun) except ValueError, e: self.intf.messageWindow(_("Error"), str(e)) continue @@ -366,9 +365,9 @@ class PartitionTypeWindow(InstallWindow): if rc != gtk.RESPONSE_CANCEL: w = self.intf.waitWindow(_("Rescanning disks"), _("Rescanning disks")) - partitions.partitionObjectsInitialize(self.anaconda) - createAllowedDrivesStore(self.diskset.disks, - self.partitions.autoClearPartDrives, + self.storage.reset() + createAllowedDrivesStore(self.storage, + self.partitions.clearPartDrives, self.drivelist, disallowDrives=[self.anaconda.updateSrc]) self._fillBootStore() @@ -381,13 +380,15 @@ class PartitionTypeWindow(InstallWindow): defaultBoot = self.anaconda.id.bootloader.drivelist[0] else: defaultBoot = None - for disk in self.diskset.disks.values(): - if not disk.device.path[5:] in self.anaconda.id.bootloader.drivelist: + for disk in self.storage.disks: + partedDisk = disk.partedDisk + if partedDisk.device.path[5:] not in self.anaconda.id.bootloader.drivelist: continue - size = disk.device.getSize(unit="MB") - dispstr = "%s %8.0f MB %s" %(disk.device.path[5:], size, disk.device.model) + size = partedDisk.device.getSize(unit="MB") + dispstr = "%s %8.0f MB %s" %(partedDisk.device.path[5:], + size, partedDisk.device.model) i = bootstore.append(None) - bootstore[i] = (dispstr, disk.device.path[5:]) + bootstore[i] = (dispstr, partedDisk.device.path[5:]) if disk.device.path[5:] == defaultBoot: self.bootcombo.set_active_iter(i) @@ -397,8 +398,7 @@ class PartitionTypeWindow(InstallWindow): def getScreen(self, anaconda): self.anaconda = anaconda - self.diskset = anaconda.id.diskset - self.partitions = anaconda.id.partitions + self.storage = anaconda.id.storage self.intf = anaconda.intf self.dispatch = anaconda.dispatch @@ -435,8 +435,8 @@ class PartitionTypeWindow(InstallWindow): self.dispatch.stepInSkipList("autopartitionexecute")): self.combo.set_active(len(opts) - 1) # yeah, it's a hack - self.drivelist = createAllowedDrivesList(self.diskset.disks, - self.partitions.autoClearPartDrives, + self.drivelist = createAllowedDrivesList(self.storage.disks, + self.storage.clearPartDrives, disallowDrives=[self.anaconda.updateSrc]) self.drivelist.set_size_request(375, 80) @@ -454,7 +454,7 @@ class PartitionTypeWindow(InstallWindow): self.review = not self.dispatch.stepInSkipList("partition") self.xml.get_widget("reviewButton").set_active(self.review) - self.xml.get_widget("encryptButton").set_active(self.partitions.autoEncrypt) + self.xml.get_widget("encryptButton").set_active(self.storage.encryptedAutoPart) active = self.combo.get_active_iter() val = self.combo.get_model().get_value(active, 1) -- cgit From 625c193192cd7dbdd4be4cfe12fcd115b79ac9db Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 23:52:26 -0600 Subject: Remove a bunch of obsolete imports. --- iw/partition_gui.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index 94d8781fa..087de9c6d 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -27,14 +27,11 @@ try: except ImportError: import gnome.canvas as gnomecanvas import pango -import autopart import gui import parted import string import types -import raid from constants import * -import lvm from iw_gui import * from flags import flags @@ -44,9 +41,6 @@ import raid_dialog_gui import partition_dialog_gui from partIntfHelpers import * -from partedUtils import * -from fsset import * -from partRequests import * from constants import * from partition_ui_helpers_gui import * -- cgit From 08fb853621902a64d1d2e7c624b361de20683403 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 23:53:05 -0600 Subject: Update to call storage.destroyDevice instead of storage.deleteDevice. Consistency makes maintenance easier. --- partIntfHelpers.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/partIntfHelpers.py b/partIntfHelpers.py index 9393cd267..23fbf00f3 100644 --- a/partIntfHelpers.py +++ b/partIntfHelpers.py @@ -133,9 +133,9 @@ def doDeleteDevice(intf, storage, device, confirm=1, quiet=0): return False for dep in storage.deviceDeps(device): - storage.deleteDevice(dep) + storage.destroyDevice(dep) - storage.deleteDevice(device) + storage.destroyDevice(device) return True def doDeleteDependentDevices(intf, storage, device, confirm=1, quiet=0): @@ -161,7 +161,7 @@ def doDeleteDependentDevices(intf, storage, device, confirm=1, quiet=0): immutable.append(dep.path) continue else: - storage.deleteDevice(dep) + storage.destroyDevice(dep) if immutable and not quiet: remaining = "\n\t" + "\n\t".join(immutable) + "\n" -- cgit From ad6daabf2a5c0f3c54478eb86b43992f2e236cfe Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 23:54:19 -0600 Subject: Remove obsolete imports. --- partedUtils.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/partedUtils.py b/partedUtils.py index 68d87d98a..f7db3e22e 100644 --- a/partedUtils.py +++ b/partedUtils.py @@ -32,12 +32,9 @@ import os, sys, string, struct, resource from product import * import exception -import fsset import iutil, isys -import raid import dmraid import block -import lvm import inspect from flags import flags from errors import * -- cgit From 3e56c67aa81d85b3baf763e80e5e7ab49cb9175c Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Feb 2009 23:56:28 -0600 Subject: Various changes, mostly to Storage. Details below. - Add a call to udev_trigger before initial scan. - Add Storage.swaps property to list swap devices. - Reinitialize Storage.autoPartitionRequest in Storage.reset. - Rename Storage.deleteDevice to Storage.destroyDevice to improve consistency. - Incorporate LV naming code from old autopart.py. - Extend Storage.newLV to take advantage of improved LV naming method. - Lift DiskSet.checkNoDisks and add it to Storage. - Add a stub for Storage.sanityCheck. --- storage/__init__.py | 90 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 66 insertions(+), 24 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 6fccba6d8..4a280e26c 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -29,6 +29,7 @@ from devices import * from deviceaction import * from formats import getFormat from devicelibs.lvm import safeLvmName +from udev import udev_trigger import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -42,11 +43,13 @@ def storageInitialize(anaconda): if anaconda.dir == DISPATCH_BACK: return + # XXX I don't understand why I have to do this + udev_trigger(subsystem="block") anaconda.storage.reset() # dispatch.py helper function def storageComplete(anaconda): - if anaconda.dir == DISPATCH_BACK and anaconda.id.storage.fsset.isActive: + if anaconda.dir == DISPATCH_BACK: rc = anaconda.intf.messageWindow(_("Installation cannot continue."), _("The storage configuration you have " "chosen has already been activated. You " @@ -136,6 +139,7 @@ class Storage(object): passphrase=self.encrpytionPassphrase, luksDict=self._luksDevs) self.fsset = FSSet(self.devicetree) + self.autoPartitionRequests = [] @property def devices(self): @@ -269,6 +273,19 @@ class Storage(object): raidMinors.remove(array.minor) return raidMinors + @property + def swaps(self): + """ A list of the swap devices in the device tree. + + This is based on the current state of the device tree and + does not necessarily reflect the actual on-disk state of the + system's disks. + """ + devices = self.devicetree.devices.values() + swaps = [d for d in devices if d.format.type == "swap"] + swaps.sort(key=lambda d: d.name) + return swaps + def exceptionDisks(self): """ Return a list of removable devices to save exceptions to. @@ -391,15 +408,21 @@ class Storage(object): return LVMVolumeGroupDevice(name, parents=pvs, peSize=peSize) - def newLV(self, vg, name=None, fmt_type=None, size=None): + def newLV(self, vg, name=None, fmt_type=None, size=None, + mountpoint=None): """ Return a new LVMLogicalVolumeDevice instance. """ if name and name in [d.name for d in self.devices]: raise ValueError("name already in use") elif not name: - name = self.createSuggestedLVName() - return LVMLogicalVolumeDevice(name, - format=getFormat(fmt_type), - size=size) + if fmt_type == "swap": + swap = True + else: + swap = False + name = self.createSuggestedLVName(vg, + swap=swap, + mountpoint=mountpoint) + format=getFormat(fmt_type, mountpoint=mountpoint), + return LVMLogicalVolumeDevice(name, format=format, size=size) def createDevice(self, device): """ Schedule creation of a device. @@ -410,8 +433,8 @@ class Storage(object): action = ActionCreateDevice(device) self.devicetree.registerAction(action) - def deleteDevice(self, device): - """ Schedule deletion of a device. """ + def destroyDevice(self, device): + """ Schedule destruction of a device. """ action = ActionDestroyDevice(device) self.devicetree.registerAction(action) @@ -484,22 +507,41 @@ class Storage(object): return tmpname - def createSuggestedLVName(self, vg): - """Given list of LV requests, come up with a reasonable LV name - """ - i = 0 - # lv.name is of the form "$VGNAME-$LVNAME" so use lv.lvname instead - lnames = [lv.lvname for lv in vg.lvs] - while 1: - tmpname = "LogVol%02d" % (i,) - if tmpname not in lnames: - break - - i += 1 - if i > 99: - tmpname = "" - - return tmpname + def createSuggestedLVName(self, vg, swap=None, mountpoint=None): + """ Return a suitable, unused name for a new logical volume. """ + if mountpoint: + # try to incorporate the mountpoint into the name + if mountpoint == '/': + lvtemplate = 'lv_root' + else: + tmp = safeLvmName(mountpoint) + lvtemplate = "lv_%s" % (tmp,) + else: + if swap: + if len(self.swaps): + lvtemplate = "lv_swap%02d" % (len(self.swaps),) + else: + lvtemplate = "lv_swap" + else: + lvtemplate = "LogVol%02d" % (len(vg.lvs),) + + return lvtemplate + + def sanityCheck(self): + """ Run a series of tests to verify the storage configuration. """ + log.warning("storage.Storage.sanityCheck is unimplemented") + return ([], []) + + def checkNoDisks(self): + """Check that there are valid disk devices.""" + if not self.disks: + self.anaconda.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.")) + return True + return False def getReleaseString(mountpoint): -- cgit From ea85b249d3324bc90d89bbc17ddb4b5475ba6f7b Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 24 Feb 2009 00:03:27 -0600 Subject: Expect to be able to find parted from now on. --- storage/devices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devices.py b/storage/devices.py index b62213cfe..1670f5a71 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -717,7 +717,7 @@ class PartitionDevice(StorageDevice): # For existing partitions we will get the size from # parted. - if self.exists and 'parted' in globals().keys(): + if self.exists: log.debug("looking up parted Partition: %s" % self.path) #self.partedPartition = parted.getPartitionByName(self.path) self._partedPartition = self.disk.partedDisk.getPartitionByPath(self.path) -- cgit From 85028569883e76514a5983939e569bdb6d5743a9 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 24 Feb 2009 00:04:52 -0600 Subject: Make it easy to specify a format's device at creation/setup time. --- storage/deviceaction.py | 3 ++- storage/formats/__init__.py | 30 ++++++++++++++++++++++++------ storage/formats/fs.py | 2 ++ storage/formats/luks.py | 11 ++--------- storage/formats/lvmpv.py | 4 +--- storage/formats/swap.py | 2 ++ 6 files changed, 33 insertions(+), 19 deletions(-) diff --git a/storage/deviceaction.py b/storage/deviceaction.py index 09c2277de..3be35a14c 100644 --- a/storage/deviceaction.py +++ b/storage/deviceaction.py @@ -230,7 +230,8 @@ class ActionCreateFormat(DeviceAction): # XXX we should set partition type flag as needed # - or should that be in the CreateDevice action? self.device.setup() - self.device.format.create(options=self.device.formatArgs) + self.device.format.create(device=self.device.path, + options=self.device.formatArgs) def cancel(self): self.device.format = self.origFormat diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py index 3abd77664..feb92f3dd 100644 --- a/storage/formats/__init__.py +++ b/storage/formats/__init__.py @@ -162,16 +162,16 @@ class DeviceFormat(object): self.exists = kwargs.get("exists") self.options = kwargs.get("options") - def setDevice(self, devspec): + def _setDevice(self, devspec): if devspec and not devspec.startswith("/"): raise ValueError("device must be a fully qualified path") self._device = devspec - def getDevice(self): + def _getDevice(self): return self._device - device = property(lambda f: f.getDevice(), - lambda f,d: f.setDevice(d), + device = property(lambda f: f._getDevice(), + lambda f,d: f._setDevice(d), doc="Full path the device this format occupies") @property @@ -212,7 +212,13 @@ class DeviceFormat(object): def create(self, *args, **kwargs): log_method_call(self, device=os.path.basename(self.device), type=self.type, status=self.status) - pass + # allow late specification of device path + device = kwargs.get("device") + if device: + self.device = device + + if not os.path.exists(device): + raise FormatCreateError("invalid device specification") def destroy(self, *args, **kwargs): log_method_call(self, device=os.path.basename(self.device), @@ -234,7 +240,19 @@ class DeviceFormat(object): def setup(self, *args, **kwargs): log_method_call(self, device=os.path.basename(self.device), type=self.type, status=self.status) - pass + if not self.exists: + raise FormatSetupError("format has not been created") + + if self.status: + return + + # allow late specification of device path + device = kwargs.get("device") + if device: + self.device = device + + if not os.path.exists(device): + raise FormatSetupError("invalid device specification") def teardown(self, *args, **kwargs): log_method_call(self, device=os.path.basename(self.device), diff --git a/storage/formats/fs.py b/storage/formats/fs.py index c5ec44a7e..0359b0b6a 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -533,6 +533,8 @@ class FS(DeviceFormat): if self.exists: raise FSError("filesystem already exists") + DeviceFormat.create(self, *args, **kwargs) + return self.format(*args, **kwargs) def setup(self, *args, **kwargs): diff --git a/storage/formats/luks.py b/storage/formats/luks.py index 3485f56ad..607b44bd9 100644 --- a/storage/formats/luks.py +++ b/storage/formats/luks.py @@ -109,15 +109,10 @@ class LUKS(DeviceFormat): """ Open, or set up, the format. """ log_method_call(self, device=os.path.basename(self.device), type=self.type, status=self.status) - if not self.exists: - raise LUKSError("format has not been created") - if not self.configured: raise LUKSError("luks device not configured") - if self.status: - return - + DeviceFormat.setup(self, *args, **kwargs) crypto.luks_open(self.device, self.mapName, passphrase=self.__passphrase, key_file=self._key_file) @@ -137,12 +132,10 @@ class LUKS(DeviceFormat): """ Create the format. """ log_method_call(self, device=os.path.basename(self.device), type=self.type, status=self.status) - if self.exists: - raise LUKSError("format already exists") - if not self.configured: raise LUKSError("luks device not configured") + DeviceFormat.create(self, *args, **kwargs) crypto.luks_format(self.device, passphrase=self.__passphrase, key_file=self._key_file, diff --git a/storage/formats/lvmpv.py b/storage/formats/lvmpv.py index ac404f303..d27819859 100644 --- a/storage/formats/lvmpv.py +++ b/storage/formats/lvmpv.py @@ -81,9 +81,7 @@ class LVMPhysicalVolume(DeviceFormat): """ Create the format. """ log_method_call(self, device=os.path.basename(self.device), type=self.type, status=self.status) - if self.exists: - raise PhysicalVolumeError("format already exists") - + DeviceFormat.create(self, *args, **kwargs) """ Consider use of -Z|--zero -f|--force or -y|--yes may be required """ diff --git a/storage/formats/swap.py b/storage/formats/swap.py index 1888b9837..6b8646d28 100644 --- a/storage/formats/swap.py +++ b/storage/formats/swap.py @@ -108,6 +108,7 @@ class SwapSpace(DeviceFormat): if self.status: return + DeviceFormat.setup(self, *args, **kwargs) swap.swapon(self.device, priority=self.priority) def teardown(self, *args, **kwargs): @@ -130,6 +131,7 @@ class SwapSpace(DeviceFormat): if self.status: raise SwapSpaceError("device exists and is active") + DeviceFormat.create(self, *args, **kwargs) swap.mkswap(self.device, label=self.label) -- cgit From 0184b44db1d50ca405f93695c02ed2f8921cdd16 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 24 Feb 2009 00:06:04 -0600 Subject: Add doAutoPartition function and fix various minor errors. - Use storage.createDevice instead of manually creating actions and registering them. - Don't remove empty extended partitions if not clearing any partitions. --- storage/partitioning.py | 154 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 136 insertions(+), 18 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index ee78985cb..6acea41ca 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -36,29 +36,147 @@ _ = lambda x: gettext.ldgettext("anaconda", x) import logging log = logging.getLogger("storage") +def doAutoPartition(anaconda): + if anaconda.dir == DISPATCH_BACK: + anaconda.id.storage.reset() + return + + # do clearpart + clearPartitions(anaconda.id.storage) + + # get a list of disks that have at least one free space region of at + # least 100MB + disks = [] + for disk in anaconda.id.storage.disks: + partedDisk = disk.partedDisk + part = disk.partedDisk.getFirstPartition() + while part: + if not part.type & parted.PARTITION_FREESPACE: + continue + + if part.getSize(unit="MB") > 100: + disks.append(disk) + break -def clearPartitions(storage, clearPartType=CLEARPART_TYPE_NONE, - clearPartDisks=[]): + # create a separate pv partition for each disk with free space + pvs = [] + for disk in disks: + part = anaconda.id.storage.newPartition(fmt_type="lvmpv", + size=1, + disks=[disk]) + part.req_grow = True + anaconda.id.storage.createDevice(part) + pvs.append(part) + + # create a vg containing all of the new pvs + vg = anaconda.id.storage.newVG(pvs=pvs) + anaconda.id.storage.createDevice(vg) + + # convert storage.autoPartitionRequests into Device instances and + # schedule them for creation + for request in storage.autoPartitionRequests: + (mountpoint, fstype, size, maxsize, grow, asvol) = request + if asvol + dev = anaconda.id.storage.newLV(vg=vg, + fmt_type=fstype, + mountpoint=mountpoint, + size=size) + else: + dev = anaconda.id.storage.newPartition(fmt_type=fstype, + size=size, + disks=disks) + + if grow: + dev.req_grow = True + if maxsize: + dev.req_max_size = maxsize + + # schedule the device for creation + anaconda.id.storage.createDevice(dev) + + # make sure preexisting broken lvm/raid configs get out of the way + + # sanity check the individual devices + log.warning("not sanity checking devices because I don't know how yet") + + # run the autopart function to allocate partitions, grow, &c + try: + doPartitioning(anaconda.id.storage) + except PartitioningWarning as msg: + if not anaconda.isKickstart: + anaconda.intf.messageWindow(_("Warnings During Automatic " + "Partitioning"), + _("Following warnings occurred during automatic " + "partitioning:\n\n%s") % (msg,), + custom_icon='warning') + else: + log.warning(msg) + except PartitioningError as msg: + # restore drives to original state + anaconda.id.storage.reset() + if not anaconda.isKickstart: + extra = "" + anaconda.dispatch.skipStep("partition", skip = 0) + else: + extra = _("\n\nPress 'OK' to exit the installer.") + anaconda.intf.messageWindow(_("Error Partitioning"), + _("Could not allocate requested partitions: \n\n" + "%s.%s") % (msg, extra), custom_icon='error') + + if anaconda.isKickstart: + sys.exit(0) + + # sanity check the collection of devices + log.warning("not sanity checking storage config because I don't know how yet") + # now do a full check of the requests + (errors, warnings) = storage.sanityCheck() + if warnings: + for warning in warnings: + log.warning(warning) + if errors: + errortxt = string.join(errors, '\n') + if anaconda.isKickstart: + extra = _("\n\nPress 'OK' to exit the installer.") + else: + extra = _("\n\nPress 'OK' to choose a different partitioning option.") + + anaconda.intf.messageWindow(_("Automatic Partitioning Errors"), + _("The following errors occurred with your " + "partitioning:\n\n%s\n\n" + "This can happen if there is not enough " + "space on your hard drive(s) for the " + "installation. %s") + % (errortxt, extra), + custom_icon='error') + # + # XXX if in kickstart we reboot + # + if anaconda.isKickstart: + anaconda.intf.messageWindow(_("Unrecoverable Error"), + _("Your system will now be rebooted.")) + sys.exit(0) + return DISPATCH_BACK + + +def clearPartitions(storage): """ Clear partitions and dependent devices from disks. Arguments: - deviceTree -- a DeviceTree instance + storage -- a storage.Storage instance Keyword arguments: - clearPartType -- a pykickstart CLEARPART_TYPE_* constant - clearPartDisks -- a list of basenames of disks to consider + None NOTES: - Needs some error handling, especially for the parted bits. - - Should use device actions instead of dev.destroy, &c """ - if not clearPartType or clearPartType == CLEARPART_TYPE_NONE: - # not much to do -- just remove any empty extended partitions - removeEmptyExtendedPartitions(deviceTree) + if not storage.clearPartType or \ + storage.clearPartType == CLEARPART_TYPE_NONE: + # not much to do return # we are only interested in partitions that physically exist @@ -70,7 +188,8 @@ def clearPartitions(storage, clearPartType=CLEARPART_TYPE_NONE, clear = False # whether or not we will clear this partition # if we got a list of disks to clear, make sure this one's on it - if clearPartDisks and not part.disk.name in clearPartDisks: + if storage.clearPartDisks and \ + part.disk.name not in storage.clearPartDisks: continue # we don't want to fool with extended partitions, freespace, &c @@ -103,22 +222,21 @@ def clearPartitions(storage, clearPartType=CLEARPART_TYPE_NONE, leaves = [d for d in devices if d.isleaf] log.debug("leaves to remove: %s" % ([d.name for d in leaves],)) for leaf in leaves: - action = ActionDestroyDevice(leaf) - deviceTree.registerAction(action) + storage.destroyDevice(leaf) devices.remove(leaf) - # XXX can/should this be moved into PartitionDevice? + # FIXME: this should be taken care of by DeviceTree.removeDevice + # or Storage.destroyDevice part.partedPartition.disk.removePartition(part.partedPartition) log.debug("partitions: %s" % [p.getDeviceNodeName() for p in part.partedPartition.disk.partitions]) disk_name = os.path.basename(part.partedPartition.disk.device.path) if disk_name not in disks: disks.append(disk_name) - action = ActionDestroyDevice(part) - deviceTree.registerAction(action) + storage.destroyDevice(part) # now remove any empty extended partitions - removeEmptyExtendedPartitions(deviceTree) + removeEmptyExtendedPartitions(storage) def removeEmptyExtendedPartitions(storage): @@ -131,7 +249,7 @@ def removeEmptyExtendedPartitions(storage): log.debug("removing empty extended partition from %s" % disk.name) extended_name = extended.getDeviceNodeName() extended = storage.devicetree.getDeviceByName(extended_name) - storage.devicetree.registerAction(ActionDestroyDevice(extended)) + storage.destroyDevice(extended) #disk.partedDisk.removePartition(extended.partedPartition) @@ -338,7 +456,7 @@ def doPartitioning(storage, exclusiveDisks=[]): device = PartitionDevice(extended.getDeviceNodeName(), parents=disk) device.setPartedPartition(extended) - storage.addDevice(device) + storage.createDevice(device) def allocatePartitions(disks, partitions): """ Allocate partitions based on requested features. -- cgit From f8b7f2be4ed499045bcd3df5d02a3c065bd2a4a3 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 24 Feb 2009 11:23:40 -0600 Subject: Lots of little updates to make things use the new storage module. exception.py: - Remove unused partedUtils import. - Collect things from storage, not partitions. gui.py: - exceptionDisks is in storage now, not diskset. installclasses/rhel.py: installclasses/fedora.py: - setDefaultPartitioning take storage arg, not partitions. iw/autopart_type.py: - Find things in storage, not partitions. iw/bootloader_main_gui.py: iw/osbootwidget.py: - Remove unused partedUtils import. - Find fsset in storage now. - Use storage, not diskset, for iterating over disks/partitions. iw/lvm_dialog_gui.py: - Fix several typos from the original storage update. iw/partition_gui.py: - Find unusedMDMembers in storage, not partitions. iw/partitionui_helpers_gui.py: - Use StorageDevice.minSize,maxSize for resize limits. - Update doUIRAIDLVMChecks to use new storage module. packages.py: - Use new storage module to list vgs for selinux hack. storage/__init__.py: - Fix FSSet.createSwapFile so it creates the file on the correct device. storage/iscsi.py: - Use new storage module to identify iscsi disks. textw/partition_text.py: textw/upgrade_text.py: - Initial update to use new storage module. yuminstall.py: - Use storage module to find space for upgrade transaction. - Use storage module to locate protected partitions to mount in doPreInstall. --- exception.py | 5 ++-- gui.py | 2 +- installclasses/fedora.py | 3 ++- installclasses/rhel.py | 2 +- iw/autopart_type.py | 4 +-- iw/bootloader_main_gui.py | 24 +++++++---------- iw/lvm_dialog_gui.py | 10 +++---- iw/osbootwidget.py | 32 +++++++++------------- iw/partition_gui.py | 2 +- iw/partition_ui_helpers_gui.py | 16 +++++------ packages.py | 5 +--- storage/__init__.py | 16 ++++++----- storage/iscsi.py | 23 +++++++--------- textw/partition_text.py | 37 +++++++++++-------------- textw/upgrade_text.py | 61 ++++++++++++++++++++---------------------- yuminstall.py | 18 +++++++------ 16 files changed, 117 insertions(+), 143 deletions(-) diff --git a/exception.py b/exception.py index 681b3ec5d..ddf82b517 100644 --- a/exception.py +++ b/exception.py @@ -35,7 +35,6 @@ import inspect import iutil import types import bdb -import partedUtils from string import joinfields from cPickle import Pickler from flags import flags @@ -183,11 +182,11 @@ class AnacondaExceptionDump: "anaconda.id.instLanguage.tz", "anaconda.id.keyboard._mods._modelDict", "anaconda.id.keyboard.modelDict", - "anaconda.id.partitions.encryptionPassphrase", + "anaconda.id.storage.encryptionPassphrase", "anaconda.id.rootPassword", "anaconda.id.tmpData", "anaconda.intf.icw.buff", - "anaconda.intf.icw.currentWindow.partitions.encryptionPassphrase", + "anaconda.intf.icw.currentWindow.storage.encryptionPassphrase", "anaconda.intf.icw.stockButtons", "dispatch.sack.excludes", ] diff --git a/gui.py b/gui.py index d224fbd0b..113d31da2 100755 --- a/gui.py +++ b/gui.py @@ -742,7 +742,7 @@ class SaveExceptionWindow: store = gtk.ListStore(gobject.TYPE_STRING, gobject.TYPE_STRING) - dests = anaconda.id.diskset.exceptionDisks(anaconda) + dests = anaconda.id.storage.exceptionDisks() if flags.livecdInstall: self.destCombo.remove_text(0) diff --git a/installclasses/fedora.py b/installclasses/fedora.py index 0eda0b5c8..1fdfb05c4 100644 --- a/installclasses/fedora.py +++ b/installclasses/fedora.py @@ -63,7 +63,8 @@ class InstallClass(BaseInstallClass): BaseInstallClass.setInstallData(self, anaconda) if not anaconda.isKickstart: - BaseInstallClass.setDefaultPartitioning(self, anaconda.id.partitions, + BaseInstallClass.setDefaultPartitioning(self, + anaconda.id.storage, CLEARPART_TYPE_LINUX) def setSteps(self, anaconda): diff --git a/installclasses/rhel.py b/installclasses/rhel.py index ddfb89700..d179372b1 100644 --- a/installclasses/rhel.py +++ b/installclasses/rhel.py @@ -89,7 +89,7 @@ class InstallClass(BaseInstallClass): BaseInstallClass.setInstallData(self, anaconda) if not anaconda.isKickstart: BaseInstallClass.setDefaultPartitioning(self, - anaconda.id.partitions, + anaconda.id.storage, CLEARPART_TYPE_LINUX) def setSteps(self, anaconda): diff --git a/iw/autopart_type.py b/iw/autopart_type.py index 8963a2c6d..1090afce8 100644 --- a/iw/autopart_type.py +++ b/iw/autopart_type.py @@ -367,7 +367,7 @@ class PartitionTypeWindow(InstallWindow): _("Rescanning disks")) self.storage.reset() createAllowedDrivesStore(self.storage, - self.partitions.clearPartDrives, + self.storage.clearPartDrives, self.drivelist, disallowDrives=[self.anaconda.updateSrc]) self._fillBootStore() @@ -428,7 +428,7 @@ class PartitionTypeWindow(InstallWindow): for (txt, val) in opts: iter = store.append(None) store[iter] = (txt, val) - if val == self.partitions.autoClearPartType: + if val == self.storage.clearPartType: self.combo.set_active_iter(iter) if ((self.combo.get_active() == -1) or diff --git a/iw/bootloader_main_gui.py b/iw/bootloader_main_gui.py index d11a73e89..c674d3261 100644 --- a/iw/bootloader_main_gui.py +++ b/iw/bootloader_main_gui.py @@ -21,7 +21,6 @@ import gtk import gobject -import partedUtils import gui from iw_gui import * from constants import * @@ -93,15 +92,13 @@ class MainBootloaderWindow(InstallWindow): combo.pack_start(cell, True) combo.set_attributes(cell, text = 0) - keys = disks.keys() - keys.sort() - - for d in keys: - size = disks[d].device.getSize(unit="MB") - m = disks[d].device.model + for disk in disks: + size = disk.size + model = disk.partedDisk.device.model i = model.append(None) - model[i] = ("%s %8.0f MB %s" %(d, size, m), "%s" %(d,)) + model[i] = ("%s %8.0f MB %s" %(disk.name, size, model), + "%s" %(disk.name,)) if d == active: combo.set_active_iter(i) @@ -113,8 +110,7 @@ class MainBootloaderWindow(InstallWindow): dialog.set_transient_for(self.parent) dialog.show() - choices = anaconda.id.fsset.bootloaderChoices(anaconda.id.diskset, - self.bl) + choices = anaconda.id.storage.fsset.bootloaderChoices(self.bl) for t in ("mbr", "boot"): if not choices.has_key(t): continue @@ -136,7 +132,7 @@ class MainBootloaderWindow(InstallWindow): lbl = dxml.get_widget("bd%dLabel" %(i,)) combo.show() lbl.show() - m = __genStore(combo, anaconda.id.diskset.disks, self.driveorder[i - 1]) + m = __genStore(combo, anaconda.id.storage.disks, self.driveorder[i - 1]) dxml.get_widget("bd1Combo").connect("changed", __driveChange, dxml, choices) __driveChange(dxml.get_widget("bd1Combo"), dxml, choices) @@ -185,10 +181,10 @@ class MainBootloaderWindow(InstallWindow): self.bl = anaconda.id.bootloader self.intf = anaconda.intf - drives = anaconda.id.diskset.disks + disks = anaconda.id.storage.disks self.driveorder = self.bl.drivelist if len(self.driveorder) == 0: - self.driveorder = drives.keys() + self.driveorder = [d.name for d in disks] if self.bl.getPassword(): self.usePass = 1 @@ -206,7 +202,7 @@ class MainBootloaderWindow(InstallWindow): else: # we don't know what it is yet... if mbr is possible, we want # it, else we want the boot dev - choices = anaconda.id.fsset.bootloaderChoices(anaconda.id.diskset, self.bl) + choices = anaconda.id.storage.fsset.bootloaderChoices(self.bl) if choices.has_key('mbr'): self.bldev = choices['mbr'][0] else: diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index 867755ab0..ea6d7e831 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -79,14 +79,12 @@ class VolumeGroupEditor: """ first = 1 pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model()) - for id in pvlist: + for pv in pvlist: try: pesize = int(self.peCombo.get_active_value()) except: pesize = 32768 - pvreq = self.partitions.getRequestByID(id) - pvsize = pvreq.getActualSize(self.partitions, self.diskset) - pvsize = lvm.clampPVSize(pvsize, pesize) - int(pesize/1024) + pvsize = lvm.clampPVSize(pv.size, pesize) - int(pesize/1024) if first: minpvsize = pvsize first = 0 @@ -143,7 +141,7 @@ class VolumeGroupEditor: if not rc: return 0 - # XXX this is very sneaky, just changing the lvs' size attributes + # XXX this is very sneaky, just changing the lvs' size attributes. # will we suffer for it? almost certainly. for lv in self.logvolreqs: osize = lv.size @@ -803,7 +801,7 @@ class VolumeGroupEditor: availSpaceMB = 0L for pv in pvlist: # XXX why the subtraction? - pvsize = lvm.clampSize(pvsize, curpe) - (curpe/1024) + pvsize = lvm.clampSize(pe.size, curpe) - (curpe/1024) # have to clamp pvsize to multiple of PE availSpaceMB = availSpaceMB + pvsize diff --git a/iw/osbootwidget.py b/iw/osbootwidget.py index 5263b853e..139714a09 100644 --- a/iw/osbootwidget.py +++ b/iw/osbootwidget.py @@ -37,7 +37,7 @@ class OSBootWidget: def __init__(self, anaconda, parent, blname = None): self.bl = anaconda.id.bootloader self.fsset = anaconda.id.fsset - self.diskset = anaconda.id.diskset + self.storage = anaconda.id.storage self.parent = parent self.intf = anaconda.intf if blname is not None: @@ -153,26 +153,20 @@ class OSBootWidget: label = gui.MnemonicLabel(_("_Device")) table.attach(label, 0, 1, 2, 3, gtk.FILL, 0, 10) if not isRoot: - # XXX should potentially abstract this out into a function - pedparts = [] - parts = [] - disks = self.diskset.disks - func = lambda part: (part.active and - part.getFlag(parted.PARTITION_LVM) != 1 and - part.getFlag(parted.PARTITION_RAID) != 1) - for drive in disks.keys(): - pedparts.extend(partedUtils.filter_partitions(disks[drive], func)) - for part in pedparts: - parts.append(part.getDeviceNodeName()) - del pedparts - parts.sort() + for part in self.storage.partitions: + if part.partedPartition.getFlag(parted.PARTITION_LVM) or \ + part.partedPartition.getFlag(parted.PARTITION_RAID) or \ + not part.active: + continue + + parts.append(part) deviceCombo = datacombo.DataComboBox() defindex = 0 i = 0 - for part in parts: - deviceCombo.append("/dev/%s" %(part,), part) - if oldDevice and oldDevice == part: + for part in parts: + deviceCombo.append(part.path, part.name) + if oldDevice and oldDevice == part.name: defindex = i i = i + 1 @@ -367,8 +361,8 @@ class OSBootWidget: continue isRoot = 0 - fsentry = self.fsset.getEntryByDeviceName(dev) - if fsentry and fsentry.getMountPoint() == '/': + rootDev = self.storage.fsset.rootDevice + if rootDev and rootDev.name == dev: isRoot = 1 iter = self.osStore.append() diff --git a/iw/partition_gui.py b/iw/partition_gui.py index 087de9c6d..239445f0e 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -1188,7 +1188,7 @@ class PartitionWindow(InstallWindow): # see if we have enough free software RAID partitions first # if no raid partitions exist, raise an error message and return - availraidparts = self.partitions.unusedMDMembers() + availraidparts = self.storage.unusedMDMembers() dialog = gtk.Dialog(_("RAID Options"), self.parent) gui.addFrame(dialog) diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index 97d45d267..fa1871220 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -352,9 +352,9 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, value = origrequest.size # TODO: sort out resizing - reqlower = origrequest.getMinimumResizeMB(storage) - requpper = origrequest.getMaximumResizeMB(partitions) - if not origrequest.format: + reqlower = origrequest.minSize + requpper = origrequest.maxSize + if origrequest.format.exists: lower = reqlower else: lower = 1 @@ -387,17 +387,17 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, return (row, rc) # do tests we just want in UI for now, not kickstart -def doUIRAIDLVMChecks(request, diskset): - fstype = request.fstype - numdrives = len(diskset.disks.keys()) +def doUIRAIDLVMChecks(request, storage): + fstype = request.format.name + numdrives = len(storage.disks) ## if fstype and fstype.getName() == "physical volume (LVM)": ## if request.grow: ## return (_("Partitions of type '%s' must be of fixed size, and " ## "cannot be marked to fill to use available space.")) % (fstype.getName(),) - if fstype and fstype.getName() in ["physical volume (LVM)", "software RAID"]: - if numdrives > 1 and (request.drive is None or len(request.drive) > 1): + if fstype in ["physical volume (LVM)", "software RAID"]: + if numdrives > 1 and (not request.req_disks or len(request.disks) > 1): return (_("Partitions of type '%s' must be constrained to " "a single drive. To do this, select the " "drive in the 'Allowable Drives' checklist.")) % (fstype.getName(),) diff --git a/packages.py b/packages.py index b34725a73..99ae83fc9 100644 --- a/packages.py +++ b/packages.py @@ -255,10 +255,7 @@ def setFileCons(anaconda): "/etc/shadow", "/etc/shadow-", "/etc/gshadow"] + \ glob.glob('/etc/dhclient-*.conf') - vgs = [] - for entry in anaconda.id.partitions.requests: - if isinstance(entry, partRequests.VolumeGroupRequestSpec): - vgs.append("/dev/%s" %(entry.volumeGroupName,)) + vgs = ["/dev/%s" % vg.name for vg in anaconda.id.storage.vgs] # ugh, this is ugly for dir in ["/etc/sysconfig/network-scripts", "/var/lib/rpm", "/etc/lvm", "/dev/mapper", "/etc/iscsi", "/var/lib/iscsi", "/root", "/var/log", "/etc/modprobe.d", "/etc/sysconfig" ] + vgs: diff --git a/storage/__init__.py b/storage/__init__.py index 4a280e26c..c8400c2d1 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1161,24 +1161,26 @@ class FSSet(object): device.teardownFormat() device.teardown() - def createSwapFile(self, device, rootPath, size): + def createSwapFile(self, rootPath, device, size): """ Create and activate a swap file under rootPath. """ filename = "/SWAP" - count = 0 - while os.path.exists("%s/%s" % (rootPath, filename)) or \ + count = 0 + basedir = os.path.normpath("%s/%s" % (rootPath, + device.format.mountpoint)) + while os.path.exists("%s/%s" % (basedir, filename)) or \ self.devicetree.getDeviceByName(filename): - file = os.path.normpath("%s/%s" % (rootPath, filename)) + file = os.path.normpath("%s/%s" % (basedir, filename)) count += 1 filename = "/SWAP-%d" % count dev = FileDevice(filename, size=size, - parents=self.rootDevice, + parents=[device], format=getFormat("swap", device=filename)) dev.create() - dev.createFormat() dev.setup() - dev.setupFormat() + dev.format.create() + dev.format.setup() # nasty, nasty self.devicetree._addDevice(dev) diff --git a/storage/iscsi.py b/storage/iscsi.py index 214a9c877..af06cfef5 100644 --- a/storage/iscsi.py +++ b/storage/iscsi.py @@ -297,24 +297,19 @@ class iscsi(object): if not flags.test: root_drives = [ ] - req = anaconda.id.partitions.getRequestByMountPoint("/") - root_requests = anaconda.id.partitions.getUnderlyingRequests(req) - for req in root_requests: - for drive in req.drive: - part = anaconda.id.diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device) - if part: - break - if not part: - continue - if drive not in root_drives: - root_drives.append(drive) + root = anaconda.id.storage.fsset.rootDevice + root_deps = anaconda.id.storage.deviceDeps(root) + for dev in root_deps: + if dev in anaconda.id.storage.disks and \ + dev not in root_drives: + root_drives.append(dev.path) log.debug("iscsi.write: root_drives: %s" % (string.join(root_drives),)) # set iscsi nodes not used for root to autostart - for disk in anaconda.id.diskset.disks.keys(): - if isys.driveIsIscsi(disk) and not disk in root_drives: - iscsi_make_node_autostart(disk) + for disk in anaconda.id.storage.disks: + if isys.driveIsIscsi(disk.path) and not disk in root_drives: + iscsi_make_node_autostart(disk.path) if not os.path.isdir(instPath + "/etc/iscsi"): os.makedirs(instPath + "/etc/iscsi", 0755) diff --git a/textw/partition_text.py b/textw/partition_text.py index ded62f283..0419f8a38 100644 --- a/textw/partition_text.py +++ b/textw/partition_text.py @@ -27,13 +27,7 @@ import string import copy import network import parted -import partitions -from partedUtils import * from partIntfHelpers import * -from partRequests import * -from fsset import * -from raid import availRaidLevels -from autopart import * from snack import * from constants_text import * from constants import * @@ -73,7 +67,7 @@ class PartitionTypeWindow: for (txt, val) in opts: typebox.append(txt, val) - typebox.setCurrent(anaconda.id.partitions.autoClearPartType) + typebox.setCurrent(anaconda.id.storage.clearPartType) g.add(typebox, 0, 1, (0, 1, 0, 0)) @@ -97,13 +91,11 @@ class PartitionTypeWindow: screen.pushHelpLine (_(",<+>,<-> selection | Add drive | next screen")) # restore the drive list each time - disks = anaconda.id.diskset.disks.keys() - disks.sort() - cleardrives = anaconda.id.partitions.autoClearPartDrives + disks = anaconda.id.storage.disks + cleardrives = anaconda.id.storage.clearPartDrives for disk in disks: - size = anaconda.id.diskset.disks[disk].device.getSize(unit="MB") - model = anaconda.id.diskset.disks[disk].device.model + model = disk.partedDisk.device.model if not cleardrives or len(cleardrives) < 1: selected = 1 @@ -113,7 +105,7 @@ class PartitionTypeWindow: else: selected = 0 - sizestr = "%8.0f MB" % (size,) + sizestr = "%8.0f MB" % (disk.size,) diskdesc = "%6s %s (%s)" % (disk, sizestr, model[:24],) drivelist.append(diskdesc, selected = selected) @@ -133,13 +125,13 @@ class PartitionTypeWindow: if rc == "F2": if self.addDriveDialog(screen) != INSTALL_BACK: - partitions.partitionObjectsInitialize(anaconda) + anaconda.id.storage.reset() continue if res == TEXT_BACK_CHECK: return INSTALL_BACK - if anaconda.id.diskset.checkNoDisks(): + if anaconda.id.storage.checkNoDisks(): continue if len(sel) < 1: @@ -147,8 +139,8 @@ class PartitionTypeWindow: continue anaconda.dispatch.skipStep("autopartitionexecute", skip = 0) - anaconda.id.partitions.autoClearPartType = partmethod_ans - anaconda.id.partitions.autoClearPartDrives = sel + anaconda.id.storage.clearPartType = partmethod_ans + anaconda.id.storage.clearPartDrives = sel break # ask to review autopartition layout - but only if it's not custom partitioning @@ -159,7 +151,7 @@ class PartitionTypeWindow: def addDriveDialog(self, screen): newdrv = [] - import iscsi + from storage import iscsi if iscsi.has_iscsi(): newdrv.append("Add iSCSI target") if iutil.isS390(): @@ -204,7 +196,7 @@ class PartitionTypeWindow: devnum = entries[0].strip() wwpn = entries[1].strip() fcplun = entries[2].strip() - self.anaconda.id.zfcp.addFCP(devnum, wwpn, fcplun) + self.anaconda.id.storage.zfcp.addFCP(devnum, wwpn, fcplun) return INSTALL_OK @@ -255,8 +247,9 @@ class PartitionTypeWindow: raise ValueError, msg iname = entries[1].strip() - if not self.anaconda.id.iscsi.initiatorSet: - self.anaconda.id.iscsi.initiator = iname - self.anaconda.id.iscsi.addTarget(ip, port, user, pw, user_in, pw_in) + if not self.anaconda.id.storage.iscsi.initiatorSet: + self.anaconda.id.storage.iscsi.initiator = iname + self.anaconda.id.storage.iscsi.addTarget(ip, port, user, pw, + user_in, pw_in) return INSTALL_OK diff --git a/textw/upgrade_text.py b/textw/upgrade_text.py index 34c08238d..65cbaeb38 100644 --- a/textw/upgrade_text.py +++ b/textw/upgrade_text.py @@ -22,7 +22,6 @@ import iutil import upgrade from constants_text import * from snack import * -from fsset import * from flags import flags from constants import * @@ -32,7 +31,7 @@ _ = lambda x: gettext.ldgettext("anaconda", x) class UpgradeMigrateFSWindow: def __call__ (self, screen, anaconda): - migent = anaconda.id.fsset.getMigratableEntries() + migent = anaconda.id.storage.fsset.getMigratableEntries() g = GridFormHelp(screen, _("Migrate File Systems"), "upmigfs", 1, 4) @@ -48,15 +47,17 @@ class UpgradeMigrateFSWindow: g.add(tb, 0, 0, anchorLeft = 1, padding = (0, 0, 0, 1)) partlist = CheckboxTree(height=4, scroll=1) - for entry in migent: - if entry.fsystem.getName() != entry.origfsystem.getName(): - migrating = 1 + for device in migent: + if not device.format.exists: + migrating = True else: - migrating = 0 + migrating = False - partlist.append("/dev/%s - %s - %s" % (entry.device.getDevice(), - entry.origfsystem.getName(), - entry.mountpoint), entry, migrating) + # FIXME: the fstype at least will be wrong here + partlist.append("%s - %s - %s" % (device.path, + device.format.type, + device.format.mountpoint), + device, migrating) g.add(partlist, 0, 1, padding = (0, 0, 0, 1)) @@ -74,29 +75,26 @@ class UpgradeMigrateFSWindow: return INSTALL_BACK # reset - for entry in migent: - entry.setFormat(0) - entry.setMigrate(0) - entry.fsystem = entry.origfsystem + # XXX the way to do this is by scheduling and cancelling actions + #for entry in migent: + # entry.setFormat(0) + # entry.setMigrate(0) + # entry.fsystem = entry.origfsystem for entry in partlist.getSelection(): try: - newfs = entry.fsystem.migratetofs[0] - newfs = fileSystemTypeGet(newfs) + newfs = getFormat(entry.format.migratetofs[0]) except Exception, e: log.info("failed to get new filesystem type, defaulting to ext3: %s" %(e,)) - newfs = fileSystemTypeGet("ext3") - entry.setFileSystemType(newfs) - entry.setFormat(0) - entry.setMigrate(1) - + newfs = getFormat("ext3") + anaconda.id.storage.migrateFormat(entry, newfs) screen.popWindow() return INSTALL_OK class UpgradeSwapWindow: def __call__ (self, screen, anaconda): - (fsList, suggSize, suggMntPoint) = anaconda.id.upgradeSwapInfo + (fsList, suggSize, suggDev) = anaconda.id.upgradeSwapInfo ramDetected = iutil.memInstalled()/1024 @@ -121,10 +119,13 @@ class UpgradeSwapWindow: _("Partition"), _("Free Space"))) count = 0 - for (mnt, part, size) in fsList: - listbox.append("%-25s /dev/%-10s %6dMB" % (mnt, part, size), count) + for (device, size) in fsList: + listbox.append("%-25s %-15s %6dMB" % (device.format.mountpoint, + device.path, + size), + count) - if (mnt == suggMntPoint): + if (device == suggDev): listbox.setCurrent(count) count = count + 1 @@ -176,7 +177,7 @@ class UpgradeSwapWindow: "valid number.")) if type(val) == type(1): - (mnt, part, size) = fsList[listbox.current()] + (dev, size) = fsList[listbox.current()] if size < (val + 16): anaconda.intf.messageWindow(_("Error"), _("There is not enough space on the " @@ -189,7 +190,7 @@ class UpgradeSwapWindow: else: screen.popWindow() if flags.setupFilesystems: - upgrade.createSwapFile(anaconda.rootPath, anaconda.id.fsset, mnt, val) + upgrade.createSwapFile(anaconda.rootPath, dev, val) anaconda.dispatch.skipStep("addswap", 1) return INSTALL_OK @@ -212,12 +213,8 @@ class UpgradeExamineWindow: else: default = 0 - for (drive, fs, desc, label, uuid) in parts: - if drive[:5] != "/dev/": - devname = "/dev/" + drive - else: - devname = drive - partList.append("%s (%s)" %(desc, drive)) + for (device, desc) in parts: + partList.append("%s (%s)" %(desc, device.path)) (button, choice) = ListboxChoiceWindow(screen, _("System to Upgrade"), _("There seem to be one or more existing Linux installations " diff --git a/yuminstall.py b/yuminstall.py index 68fd5a191..490c97e0b 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -1358,14 +1358,16 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon (self.dlpkgs, self.totalSize, self.totalFiles) = self.ayum.getDownloadPkgs() if not anaconda.id.getUpgrade(): - usrPart = anaconda.id.partitions.getRequestByMountPoint("/usr") + usrPart = None + for fs in anaconda.id.storage.devicetree.filesystems: + if fs.mountpoint == "/usr": + usrPart = fs if usrPart is not None: largePart = usrPart else: - largePart = anaconda.id.partitions.getRequestByMountPoint("/") + largePart = anaconda.id.storage.fsset.rootDevice - if largePart and \ - largePart.getActualSize(anaconda.id.partitions, anaconda.id.diskset) < self.totalSize / 1024: + if largePart and largePart.size < self.totalSize / 1024: rc = anaconda.intf.messageWindow(_("Error"), _("Your selected packages require %d MB " "of free space for installation, but " @@ -1443,12 +1445,12 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon # If there are any protected partitions we want to mount, create their # mount points now. - protected = anaconda.id.partitions.protectedPartitions() + protected = anaconda.id.storage.protectedPartitions() if protected: for protectedDev in protected: - request = anaconda.id.partitions.getRequestByDeviceName(protectedDev) - if request and request.mountpoint: - dirList.append(request.mountpoint) + request = anaconda.id.storage.devicetree.getDeviceByName(protectedDev) + if request and request.format.mountpoint: + dirList.append(request.format.mountpoint) for i in dirList: try: -- cgit From 7ac7bcab347ea4ac3c7ca6c87a204a861eff814b Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 26 Feb 2009 18:03:41 -0600 Subject: Fix paths in udev rules so they work in the installer environment. --- 70-anaconda.rules | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/70-anaconda.rules b/70-anaconda.rules index b203ed26b..97d0d2381 100644 --- a/70-anaconda.rules +++ b/70-anaconda.rules @@ -3,7 +3,7 @@ SUBSYSTEM!="block", GOTO="anaconda_end" KERNEL!="dm-*", GOTO="anaconda_raid_probe" -IMPORT{program}="/sbin/dmsetup info -c --nameprefixes --unquoted --rows --noheadings -o name,uuid,suspended,readonly,major,minor,open,tables_loaded -j%M -m%m" +IMPORT{program}="/usr/sbin/dmsetup info -c --nameprefixes --unquoted --rows --noheadings -o name,uuid,suspended,readonly,major,minor,open,tables_loaded -j%M -m%m" ENV{DM_NAME}!="?*", GOTO="anaconda_end" SYMLINK+="disk/by-id/dm-name-$env{DM_NAME}" @@ -18,12 +18,12 @@ ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+ LABEL="anaconda_raid_probe" # probe raid metadata of mdraid member devices -ENV{ID_FS_TYPE}=="linux_raid_member", IMPORT{program}="/sbin/mdadm --examine --export $root/%k" +ENV{ID_FS_TYPE}=="linux_raid_member", IMPORT{program}="/usr/sbin/mdadm --examine --export $root/%k" # probe metadata of LVM2 physical volumes -ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/sbin/lvm pvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -opv_name,pv_uuid,pv_size,vg_name,vg_uuid,pv_pe_count,pv_pe_alloc_count,pe_start $root/%k" -ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/sbin/lvm vgs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -ouuid,size,free,extent_size,extent_count,free_count,pv_count $env{LVM2_VG_NAME}" -ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/sbin/lvm lvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -olv_name,lv_uuid,lv_size $env{LVM2_VG_NAME}" +ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/usr/sbin/lvm pvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -opv_name,pv_uuid,pv_size,vg_name,vg_uuid,pv_pe_count,pv_pe_alloc_count,pe_start $root/%k" +ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/usr/sbin/lvm vgs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -ouuid,size,free,extent_size,extent_count,free_count,pv_count $env{LVM2_VG_NAME}" +ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/usr/sbin/lvm lvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -olv_name,lv_uuid,lv_size $env{LVM2_VG_NAME}" LABEL="anaconda_end" -- cgit From 2500aecef62d11d82c9fd21140c3f521a6ee5855 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 26 Feb 2009 18:04:30 -0600 Subject: Add storage subdirectory to pychecker's list. --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index af327216a..deacdef23 100644 --- a/Makefile +++ b/Makefile @@ -129,7 +129,7 @@ src: archive @rm -f anaconda-$(VERSION).tar.bz2 pycheck: - PYTHONPATH=$(PYCHECKERPATH) pychecker $(PYCHECKEROPTS) *.py textw/*.py iw/*.py installclasses/*.py | grep -v "__init__() not called" + PYTHONPATH=$(PYCHECKERPATH) pychecker $(PYCHECKEROPTS) *.py textw/*.py iw/*.py installclasses/*.py storage/*.py | grep -v "__init__() not called" pycheck-file: PYTHONPATH=.:$(PYCHECKERPATH) pychecker $(PYCHECKEROPTS) $(CHECK) | grep -v "__init__() not called" -- cgit From addec40bb65f80a7f38166c5c87561d362b31fc9 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 26 Feb 2009 18:05:17 -0600 Subject: Dirty (temporary) hack to copy udev rules from updates.img into place. --- anaconda | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/anaconda b/anaconda index 199168b05..5958a2a27 100755 --- a/anaconda +++ b/anaconda @@ -170,6 +170,10 @@ def setupPythonUpdates(): f), "/tmp/updates/%s/%s" %(pypkg, f)) + import shutil + shutil.copyfile("/tmp/updates/70-anaconda.rules", + "/etc/udev/rules.d/70-anaconda.rules") + def parseOptions(): def resolution_cb (option, opt_str, value, parser): parser.values.runres = value -- cgit From 77e052201c1aa258def5fdde5c9b8745b79611f2 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 10:18:46 -0600 Subject: Lots of minor fixes and cleanups. A non-exhaustive list follows. - Change minsize/maxsize to minSize/maxSize since that's the convention elsewhere throughout the code. - Redirect output from all external utilities to tty5, not /dev/null. - Don't waste calls to basename for debug log statements, especially when the device can be None. - Add lots of missing imports. - Fix lots of remnants of previous code usage. --- autopart.py | 4 --- bootloader.py | 1 - dispatch.py | 1 - installclass.py | 1 - instdata.py | 3 +- iw/autopart_type.py | 6 ++-- iw/examine_gui.py | 8 ++--- iw/osbootwidget.py | 2 -- iw/partition_gui.py | 50 +++++++++++++------------- iw/upgrade_swap_gui.py | 16 ++++----- packages.py | 5 ++- partIntfHelpers.py | 11 +++--- partedUtils.py | 1 - storage/__init__.py | 75 ++++++++++++++++++++++++--------------- storage/devicelibs/crypto.py | 30 ++++++++-------- storage/devicelibs/dm.py | 6 ++-- storage/devicelibs/lvm.py | 65 +++++++++++++++++----------------- storage/devicelibs/mdraid.py | 26 +++++++------- storage/devicelibs/swap.py | 16 +++++---- storage/devicetree.py | 2 +- storage/formats/__init__.py | 52 ++++++++++++++++----------- storage/formats/dmraid.py | 8 ++--- storage/formats/fs.py | 83 +++++++++++++++++++++++++------------------- storage/formats/luks.py | 18 ++++++---- storage/formats/lvmpv.py | 17 ++++----- storage/formats/mdraid.py | 7 ++-- storage/formats/swap.py | 16 ++++++--- storage/udev.py | 1 - textw/upgrade_text.py | 2 +- upgrade.py | 10 +++--- yuminstall.py | 8 ++--- 31 files changed, 297 insertions(+), 254 deletions(-) diff --git a/autopart.py b/autopart.py index c447b3db3..a74dac7cf 100644 --- a/autopart.py +++ b/autopart.py @@ -23,13 +23,9 @@ import parted import copy import string, sys -import fsset -import lvm import logging from anaconda_log import logger, logFile -import cryptodev import partedUtils -import partRequests from constants import * from errors import * diff --git a/bootloader.py b/bootloader.py index 7ad11b4d5..99dada4eb 100644 --- a/bootloader.py +++ b/bootloader.py @@ -37,7 +37,6 @@ log = logging.getLogger("anaconda") import booty import bootloaderInfo -from fsset import * def bootloaderSetupChoices(anaconda): if anaconda.dir == DISPATCH_BACK: diff --git a/dispatch.py b/dispatch.py index 7501bd145..262adc1ad 100644 --- a/dispatch.py +++ b/dispatch.py @@ -24,7 +24,6 @@ import string from types import * from constants import * from packages import writeKSConfiguration, turnOnFilesystems -from packages import doMigrateFilesystems from packages import doPostAction from packages import copyAnacondaLogs from packages import firstbootConfiguration diff --git a/installclass.py b/installclass.py index a028a0a06..91f6c23b4 100644 --- a/installclass.py +++ b/installclass.py @@ -29,7 +29,6 @@ import imputil import types from instdata import InstallData -from autopart import getAutopartitionBoot, autoCreatePartitionRequests, autoCreateLVMPartitionRequests from constants import * from filer import * diff --git a/instdata.py b/instdata.py index 86f389363..c0aaf924d 100644 --- a/instdata.py +++ b/instdata.py @@ -98,7 +98,8 @@ class InstallData: method = self.anaconda.methodstr[3:] devspec = method.split(":", 3)[0] - device = storage.resolveDevice(devspec) + # XXX might as well move resolveDevice into DeviceTree + device = storage.resolveDevice(self.storage.devicetree, devspec) if device is None: if self.getUpgrade(): return diff --git a/iw/autopart_type.py b/iw/autopart_type.py index 1090afce8..0c8e6c9c1 100644 --- a/iw/autopart_type.py +++ b/iw/autopart_type.py @@ -159,7 +159,7 @@ class PartitionTypeWindow(InstallWindow): mustHaveSelectedDrive(self.intf) raise gui.StayOnScreen - self.storage.clearPartDrives = allowdrives + self.storage.clearPartDisks = allowdrives # pop the boot device to be first in the drive list defiter = self.bootcombo.get_active_iter() @@ -367,7 +367,7 @@ class PartitionTypeWindow(InstallWindow): _("Rescanning disks")) self.storage.reset() createAllowedDrivesStore(self.storage, - self.storage.clearPartDrives, + self.storage.clearPartDisks, self.drivelist, disallowDrives=[self.anaconda.updateSrc]) self._fillBootStore() @@ -436,7 +436,7 @@ class PartitionTypeWindow(InstallWindow): self.combo.set_active(len(opts) - 1) # yeah, it's a hack self.drivelist = createAllowedDrivesList(self.storage.disks, - self.storage.clearPartDrives, + self.storage.clearPartDisks, disallowDrives=[self.anaconda.updateSrc]) self.drivelist.set_size_request(375, 80) diff --git a/iw/examine_gui.py b/iw/examine_gui.py index 63abbade2..334f3d1e4 100644 --- a/iw/examine_gui.py +++ b/iw/examine_gui.py @@ -123,15 +123,11 @@ class UpgradeExamineWindow (InstallWindow): self.upgradecombo.pack_start(cell, True) self.upgradecombo.set_attributes(cell, markup=0) - for (part, filesystem, desc, label, uuid) in self.parts: + for (dev, desc) in self.parts: iter = model.append() if (desc is None) or len(desc) < 1: desc = _("Unknown Linux system") - if part[:5] != "/dev/": - devname = "/dev/" + part - else: - devname = part - model[iter][0] = "%s (%s)" %(desc, devname) + model[iter][0] = "%s (%s)" %(desc, dev.path) upboxtmp.pack_start(self.uplabel) diff --git a/iw/osbootwidget.py b/iw/osbootwidget.py index 139714a09..24f262695 100644 --- a/iw/osbootwidget.py +++ b/iw/osbootwidget.py @@ -23,7 +23,6 @@ import gtk import gobject import iutil import parted -import partedUtils import gui import datacombo from constants import * @@ -36,7 +35,6 @@ class OSBootWidget: def __init__(self, anaconda, parent, blname = None): self.bl = anaconda.id.bootloader - self.fsset = anaconda.id.fsset self.storage = anaconda.id.storage self.parent = parent self.intf = anaconda.intf diff --git a/iw/partition_gui.py b/iw/partition_gui.py index 239445f0e..48e32ca55 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -33,6 +33,7 @@ import string import types from constants import * +import storage from iw_gui import * from flags import flags @@ -604,7 +605,7 @@ class PartitionWindow(InstallWindow): return rc def getNext(self): - (errors, warnings) = self.storage.sanityCheckAllRequests() + (errors, warnings) = self.storage.sanityCheck() if errors: labelstr1 = _("The partitioning scheme you requested " "caused the following critical errors.") @@ -799,15 +800,15 @@ class PartitionWindow(InstallWindow): sectorsPerCyl = heads * sectors part = disk.partedDisk.getFirstPartition() + extendedParent = None while part: if part.type & parted.PARTITION_METADATA: part = part.nextPartition() continue - extendedParent = None partName = part.getDeviceNodeName() device = self.storage.devicetree.getDeviceByName(partName) - if not device: + if not device and not part.type & parted.PARTITION_FREESPACE: raise RuntimeError("can't find partition %s in device" " tree" % partName) @@ -817,13 +818,13 @@ class PartitionWindow(InstallWindow): continue stripe.add(part) - if device.isExtended: + if device and device.isExtended: if extendedParent: raise RuntimeError, ("can't handle more than " "one extended partition per disk") extendedParent = self.tree.append(parent) iter = extendedParent - elif device.isLogical: + elif device and device.isLogical: if not extendedParent: raise RuntimeError, ("crossed logical partition " "before extended") @@ -833,20 +834,22 @@ class PartitionWindow(InstallWindow): iter = self.tree.append(parent) self.tree[iter]['IsLeaf'] = True - if device.format.type == "luks": + if device and device.format.type == "luks": # look up the mapped/decrypted device in the tree # the format we care about will be on it dm_dev = self.storage.devicetree.getChildren(device)[0] format = dm_dev.format - else: + elif device: format = device.format + else: + format = None - if format.mountable and format.mountpoint: + if format and format.mountable and format.mountpoint: self.tree[iter]['Mount Point'] = format.mountpoint else: self.tree[iter]['Mount Point'] = "" - if format.type == "lvmpv": + if format and format.type == "lvmpv": vg = None for _vg in self.storage.vgs: if _vg.dependsOn(part): @@ -865,15 +868,15 @@ class PartitionWindow(InstallWindow): if device.format.type == "luks" and \ not device.format.exists: self.tree[iter]['Format'] = self.lock_pixbuf - elif format.exists: + elif not format.exists: self.tree[iter]['Format'] = self.checkmark_pixbuf - if format.type: + if format and format.type: self.tree[iter]['IsFormattable'] = device.format.formattable - if device.isExtended: + if device and device.isExtended: ptype = _("Extended") - elif format.type == "mdmember": + elif format and format.type == "mdmember": ptype = _("software RAID") mds = self.storage.mdarrays array = None @@ -901,7 +904,7 @@ class PartitionWindow(InstallWindow): not device.format.exists: self.tree[iter]['Format'] = self.lock_pixbuf else: - if format.type: + if format and format.type: ptype = format.name else: ptype = _("None") @@ -910,7 +913,7 @@ class PartitionWindow(InstallWindow): else: devname = "%s" % device.path self.tree[iter]['Device'] = devname - if format.exists and + if format and format.exists and \ getattr(format, "label", None): self.tree[iter]['Label'] = "%s" % format.label else: @@ -932,7 +935,8 @@ class PartitionWindow(InstallWindow): self.treeView.expand_all() def treeActivateCB(self, view, path, col): - if self.tree.getCurrentPartition(): + if isinstance(self.tree.getCurrentDevice(), + storage.devices.PartitionDevice): self.editCB() def treeSelectCB(self, selection, *args): @@ -1028,7 +1032,7 @@ class PartitionWindow(InstallWindow): def editCB(self, *args): device = self.tree.getCurrentDevice() if not device: - intf.messageWindow(_("Unable To Edit"), + self.intf.messageWindow(_("Unable To Edit"), _("You must select a device to edit"), custom_icon="error") return @@ -1036,8 +1040,8 @@ class PartitionWindow(InstallWindow): reason = self.storage.deviceImmutable(device) if reason: self.intf.messageWindow(_("Unable To Edit"), - _("You cannot edit this device:\n\n") - reason, + _("You cannot edit this device:\n\n%s") + % reason, custom_icon="error") return @@ -1047,7 +1051,7 @@ class PartitionWindow(InstallWindow): self.editLVMVolumeGroup(device) elif device.type == "lvmlv": self.editLVMVolumeGroup(device) - elif device.type = "partition": + elif device.type == "partition": self.editPartition(device) # isNew implies that this request has never been successfully used before @@ -1122,7 +1126,7 @@ class PartitionWindow(InstallWindow): # we don't really need to pass in self.storage if we're passing # self.anaconda already vgeditor = lvm_dialog_gui.VolumeGroupEditor(self.anaconda, - self.storage + self.storage, self.intf, self.parent, device, @@ -1161,10 +1165,8 @@ class PartitionWindow(InstallWindow): custom_icon="error") return - request = VolumeGroupRequestSpec(format=True) vg = self.storage.newVG() self.editLVMVolumeGroup(vg, isNew = 1) - return def makeraidCB(self, widget): @@ -1272,7 +1274,7 @@ class PartitionWindow(InstallWindow): array = self.storage.newMDArray(fmt_type=self.storage.defaultFSType) self.editRaidArray(array, isNew=1) else: - cloneDialog = raid_dialog_gui.RaidCloneDialog(self.storage + cloneDialog = raid_dialog_gui.RaidCloneDialog(self.storage, self.intf, self.parent) if cloneDialog is None: diff --git a/iw/upgrade_swap_gui.py b/iw/upgrade_swap_gui.py index 83e27c6dd..5197d9ea4 100644 --- a/iw/upgrade_swap_gui.py +++ b/iw/upgrade_swap_gui.py @@ -50,9 +50,8 @@ class UpgradeSwapWindow (InstallWindow): selection = self.view.get_selection() (model, iter) = selection.get_selected() if iter: - mnt = model.get_value(iter, 0) - part = model.get_value(iter, 1) - size = int(model.get_value(iter, 2)) + dev = model.get_value(iter, 0) + size = model.get_value(iter, 1) val = int(self.entry.get_text()) else: raise RuntimeError, "unknown value for upgrade swap location" @@ -67,7 +66,7 @@ class UpgradeSwapWindow (InstallWindow): else: if flags.setupFilesystems: - upgrade.createSwapFile(self.instPath, self.fsset, mnt, val) + self.fsset.createSwapFile(self.instPath, dev, val) self.dispatch.skipStep("addswap", 1) return None @@ -80,7 +79,7 @@ class UpgradeSwapWindow (InstallWindow): def getScreen (self, anaconda): self.neededSwap = 0 - self.fsset = anaconda.id.fsset + self.fsset = anaconda.id.storage.fsset self.instPath = anaconda.rootPath self.intf = anaconda.intf self.dispatch = anaconda.dispatch @@ -129,11 +128,10 @@ class UpgradeSwapWindow (InstallWindow): gobject.TYPE_STRING, gobject.TYPE_STRING) - for (mnt, part, size) in fsList: + for (dev, size) in fsList: iter = self.store.append() - self.store.set_value(iter, 0, mnt) - self.store.set_value(iter, 1, part) - self.store.set_value(iter, 2, str(size)) + self.store.set_value(iter, 0, dev) + self.store.set_value(iter, 1, str(size)) self.view=gtk.TreeView(self.store) label.set_mnemonic_widget(self.view) diff --git a/packages.py b/packages.py index 99ae83fc9..5e51c8135 100644 --- a/packages.py +++ b/packages.py @@ -31,14 +31,13 @@ import time import sys import string import language -import fsset -import lvm import shutil import traceback from flags import flags from product import * from constants import * from upgrade import bindMountDevDirectory +from storage.errors import * import logging log = logging.getLogger("anaconda") @@ -80,7 +79,7 @@ def copyAnacondaLogs(anaconda): log.info("Copying anaconda logs") for (fn, dest) in (("/tmp/anaconda.log", "anaconda.log"), ("/tmp/syslog", "anaconda.syslog"), - ("/tmp/X.log", "anaconda.xlog")): + ("/tmp/X.log", "anaconda.xlog", "/tmp/storage.log")): if os.access(fn, os.R_OK): try: shutil.copyfile(fn, "%s/var/log/%s" %(anaconda.rootPath, dest)) diff --git a/partIntfHelpers.py b/partIntfHelpers.py index 23fbf00f3..080c0cfa5 100644 --- a/partIntfHelpers.py +++ b/partIntfHelpers.py @@ -28,6 +28,7 @@ import string from constants import * import parted import iutil +from storage.formats import getFormat import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -155,7 +156,7 @@ def doDeleteDependentDevices(intf, storage, device, confirm=1, quiet=0): # nothing to do return False - immmutable = [] + immutable = [] for dep in deps: if storage.deviceImmutable(dep): immutable.append(dep.path) @@ -164,10 +165,10 @@ def doDeleteDependentDevices(intf, storage, device, confirm=1, quiet=0): storage.destroyDevice(dep) if immutable and not quiet: - remaining = "\n\t" + "\n\t".join(immutable) + "\n" + remaining = "\t" + "\n\t".join(immutable) + "\n" intf.messageWindow(_("Notice"), _("The following partitions were not deleted " - "because they are in use:\n\n%s") % outlist, + "because they are in use:\n\n%s") % remaining, custom_icon="warning") return True @@ -179,7 +180,7 @@ def checkForSwapNoMatch(anaconda): # this is only for existing partitions continue - if device.partType & parted.PARTITION_SWAP and \ + if device.getFlag(parted.PARTITION_SWAP) and \ not device.format.type == "swap": rc = anaconda.intf.messageWindow(_("Format as Swap?"), _("%s has a partition type of 0x82 " @@ -288,7 +289,7 @@ def confirmDelete(intf, device): "will be lost!") % device.name) elif device.type == "lvmlv": errmsg = (_("You are about to delete the logical volume \"%s\".") - % (device.name) + % device.name) elif device.type == "mdarray": errmsg = _("You are about to delete a RAID device.") elif device.type == "partition": diff --git a/partedUtils.py b/partedUtils.py index f7db3e22e..6d33a98bc 100644 --- a/partedUtils.py +++ b/partedUtils.py @@ -33,7 +33,6 @@ import os, sys, string, struct, resource from product import * import exception import iutil, isys -import dmraid import block import inspect from flags import flags diff --git a/storage/__init__.py b/storage/__init__.py index c8400c2d1..e521723b5 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -22,14 +22,27 @@ import os import time +import stat +import errno +import sys +import isys import iutil +from constants import * +from pykickstart.constants import * + +import storage_log from errors import * from devices import * +from devicetree import DeviceTree from deviceaction import * from formats import getFormat +from formats import get_device_format_class +from formats import get_default_filesystem_type from devicelibs.lvm import safeLvmName from udev import udev_trigger +import iscsi +import zfcp import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -38,14 +51,14 @@ import logging log = logging.getLogger("storage") def storageInitialize(anaconda): - anaconda.storage.shutdown() + anaconda.id.storage.shutdown() if anaconda.dir == DISPATCH_BACK: return # XXX I don't understand why I have to do this udev_trigger(subsystem="block") - anaconda.storage.reset() + anaconda.id.storage.reset() # dispatch.py helper function def storageComplete(anaconda): @@ -84,13 +97,13 @@ class Storage(object): # storage configuration variables self.ignoredDisks = [] self.exclusiveDisks = [] - self.doAutoPart = None - self.clearPartType = None + self.doAutoPart = False + self._clearPartType = CLEARPART_TYPE_NONE self.clearPartDisks = [] - self.encryptedAutoPart = None + self.encryptedAutoPart = False self.encryptionPassphrase = None - self.encryptionRetrofit = None - self.reinitializeDisks = None + self.encryptionRetrofit = False + self.reinitializeDisks = False self.zeroMbr = None self.protectedPartitions = [] self.autoPartitionRequests = [] @@ -111,7 +124,7 @@ class Storage(object): def shutdown(self): try: - self.deviceTree.teardownAll() + self.devicetree.teardownAll() except Exception as e: log.error("failure tearing down device tree: %s" % e) @@ -385,7 +398,7 @@ class Storage(object): return PartitionDevice("req%d" % self.nextID, format=getFormat(fmt_type), size=size, - parent=disks, + parents=disks, exists=False) def newMDArray(self, fmt_type=None): @@ -421,8 +434,8 @@ class Storage(object): name = self.createSuggestedLVName(vg, swap=swap, mountpoint=mountpoint) - format=getFormat(fmt_type, mountpoint=mountpoint), - return LVMLogicalVolumeDevice(name, format=format, size=size) + format = getFormat(fmt_type, mountpoint=mountpoint) + return LVMLogicalVolumeDevice(name, vg, format=format, size=size) def createDevice(self, device): """ Schedule creation of a device. @@ -620,9 +633,10 @@ def findExistingRootDevices(anaconda, upgradeany=False): continue if os.access(anaconda.rootPath + "/etc/fstab", os.R_OK): - relstr = getReleaseString(anaconda.rootPath) - if upgradeany or productMatches(relstr, productName): - rootDevs.append((device, relstr)) + (product, version) = getReleaseString(anaconda.rootPath) + if upgradeany or \ + anaconda.id.instClass.productUpgradable(product, version): + rootDevs.append((device, "%s %s" % (product, version))) # this handles unmounting the filesystem device.teardown(recursive=True) @@ -950,8 +964,8 @@ class FSSet(object): else: # nodev filesystem -- preserve or drop completely? format = getFormat(fstype) - if isinstance(format, NoDevFS): - device = NoDevice(fs) + if isinstance(format, get_device_format_class("nodev")): + device = NoDevice(format) else: device = Device(devspec) @@ -1023,7 +1037,7 @@ class FSSet(object): for device in self.swapDevices: try: device.setup() - device.setupFormat() + device.format.setup() except SuspendError: if intf: if upgrading: @@ -1067,12 +1081,13 @@ class FSSet(object): skipRoot=False): intf = anaconda.intf for device in [d for d in self.devices if d.isleaf]: - if not device.format.mountable: + if not device.format.mountable or not device.format.mountpoint: continue - if skipRoot and mountpoint == "/": + if skipRoot and device.format.mountpoint == "/": continue + options = device.format.options if "noauto" in options.split(","): continue @@ -1086,7 +1101,8 @@ class FSSet(object): options = "%s,%s" % (options, readOnly) try: - device.format.mount(options=options, chroot=anaconda.rootPath) + device.format.setup(options=options, + chroot=anaconda.rootPath) except OSError as (num, msg): if intf: if num == errno.EEXIST: @@ -1097,7 +1113,8 @@ class FSSet(object): "This is a fatal error and the " "install cannot continue.\n\n" "Press to exit the " - "installer.") % (mountpoint,)) + "installer.") + % (device.format.mountpoint,)) else: intf.messageWindow(_("Invalid mount point"), _("An error occurred when trying " @@ -1105,8 +1122,8 @@ class FSSet(object): "a fatal error and the install " "cannot continue.\n\n" "Press to exit the " - "installer.") % (mountpoint, - msg)) + "installer.") + % (device.format.mountpoint, msg)) log.error("OSError: (%d) %s" % (num, msg) ) sys.exit(0) except SystemError as (num, msg): @@ -1119,7 +1136,7 @@ class FSSet(object): "continue installation, but " "there may be problems.") % (device.path, - mountpoint), + device.format.mountpoint), type="custom", custom_icon="warning", custom_buttons=[_("_Exit installer"), @@ -1140,8 +1157,10 @@ class FSSet(object): "a fatal error and the install " "cannot continue.\n\n" "Press to exit the " - "installer.") % (mountpoint, - msg)) + "installer.") + % (device.path, + device.format.mountpoint, + msg)) log.error("FSError: %s" % msg) sys.exit(0) @@ -1186,7 +1205,7 @@ class FSSet(object): def mkDevRoot(self, instPath): root = self.rootDevice - dev = "%s/dev/%s" % (instPath, root.device.getDevice()) + dev = "%s/%s" % (instPath, root.path) 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) @@ -1277,7 +1296,7 @@ class FSSet(object): # """ % time.asctime() - for device in self.device: + for device in self.devices: # why the hell do we put swap in the fstab, anyway? if not device.format.mountable and device.format.type != "swap": continue diff --git a/storage/devicelibs/crypto.py b/storage/devicelibs/crypto.py index 0872ed052..d69e7d3ac 100644 --- a/storage/devicelibs/crypto.py +++ b/storage/devicelibs/crypto.py @@ -30,8 +30,8 @@ _ = lambda x: gettext.ldgettext("anaconda", x) def is_luks(device): rc = iutil.execWithRedirect("cryptsetup", ["isLuks", device], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath = 1) if rc: return False @@ -41,15 +41,15 @@ def is_luks(device): def luks_uuid(device): uuid = iutil.execWithCapture("cryptsetup", ["luksUUID", device], - stderr="/dev/null") + stderr="/dev/tty5") return uuid.strip() def luks_status(name): """0 means active, 1 means inactive (or non-existent)""" rc = iutil.execWithRedirect("cryptsetup", ["status", name], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath = 1) return rc @@ -78,8 +78,8 @@ def luks_format(device, rc = iutil.execWithRedirect("cryptsetup", argv, stdin = p[0], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath = 1) os.close(p[0]) @@ -100,8 +100,8 @@ def luks_open(device, name, passphrase=None, key_file=None): rc = iutil.execWithRedirect("cryptsetup", argv, stdin = p[0], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath = 1) os.close(p[0]) @@ -111,8 +111,8 @@ def luks_open(device, name, passphrase=None, key_file=None): def luks_close(name): rc = iutil.execWithRedirect("cryptsetup", ["luksClose", name], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath = 1) if rc: @@ -147,8 +147,8 @@ def luks_add_key(device, device, new_key_spec], stdin = p[0], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath = 1) os.close(p[0]) @@ -184,8 +184,8 @@ def luks_remove_key(device, device, del_key_spec], stdin = p[0], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath = 1) os.close(p[0]) diff --git a/storage/devicelibs/dm.py b/storage/devicelibs/dm.py index 02c88f614..74d8228de 100644 --- a/storage/devicelibs/dm.py +++ b/storage/devicelibs/dm.py @@ -39,7 +39,7 @@ def name_from_dm_node(dm_node): ["info", "--columns", "--noheadings", "-o", "name", "-j", str(major), "-m", str(minor)], - stderr="/dev/null") + stderr="/dev/tty5") log.debug("name_from_dm(%s) returning '%s'" % (dm_node, name.strip())) return name.strip() @@ -49,7 +49,7 @@ def dm_node_from_name(map_name): "--noheadings", "-o", "devno", map_name], - stderr="/dev/null") + stderr="/dev/tty5") (major, sep, minor) = devnum.strip().partition(":") if not sep: raise DMError("dm device does not exist") @@ -65,7 +65,7 @@ def _get_backing_devnums_from_map(map_name): "--noheadings", "-o", "devnos_used", map_name], - stderr="/dev/null") + stderr="/dev/tty5") dev_nums = buf.split() for dev_num in dev_nums: (major, colon, minor) = dev_num.partition(":") diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py index 14a11577f..cd3970745 100644 --- a/storage/devicelibs/lvm.py +++ b/storage/devicelibs/lvm.py @@ -22,6 +22,7 @@ import os import math +import re import iutil @@ -72,10 +73,10 @@ def getMaxLVSize(pe): else: return (16*1024*1024) #Max is 16TiB -def safeLvmName(str): - tmp = string.strip(str) +def safeLvmName(name): + tmp = name.strip() tmp = tmp.replace("/", "_") - tmp = re.sub("[^0-9a-zA-Z._]", "", str) + tmp = re.sub("[^0-9a-zA-Z._]", "", tmp) tmp = tmp.lstrip("_") return tmp @@ -110,8 +111,8 @@ def clampSize(size, pesize, roundup=None): def pvcreate(device): rc = iutil.execWithRedirect("lvm", ["pvcreate", device], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath=1) if rc: raise LVMError("pvcreate failed for %s" % device) @@ -122,8 +123,8 @@ def pvresize(device, size): ["pvresize", "--setphysicalvolumesize", size_arg, device], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath=1) if rc: raise LVMError("pvresize failed for %s" % device) @@ -131,8 +132,8 @@ def pvresize(device, size): def pvremove(device): rc = iutil.execWithRedirect("lvm", ["pvremove", device], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath=1) if rc: raise LVMError("pvremove failed for %s" % device) @@ -153,7 +154,7 @@ def pvinfo(device): "-o", "pv_name,pv_mda_count,vg_name,vg_uuid", device], - stderr = "/dev/null") + stderr = "/dev/tty5") vals = rc.split() if not vals: raise LVMError("pvinfo failed for %s" % device) @@ -172,8 +173,8 @@ def vgcreate(vg_name, pvs, pe_size): argv.append(pv_list) rc = iutil.execWithRedirect("lvm", argv, - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath=1) if rc: @@ -181,8 +182,8 @@ def vgcreate(vg_name, pvs, pe_size): def vgremove(vg_name): rc = iutil.execWithRedirect("lvm", ["vgremove", vg_name], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath=1) if rc: @@ -190,16 +191,16 @@ def vgremove(vg_name): def vgactivate(vg_name): rc = iutil.execWithRedirect("lvm", ["vgchange" "-a", "y", vg_name], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath=1) if rc: raise LVMError("vgactivate failed for %s" % vg_name) def vgdeactivate(vg_name): rc = iutil.execWithRedirect("lvm", ["vgchange", "-a", "n", vg_name], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath=1) if rc: @@ -208,8 +209,8 @@ def vgdeactivate(vg_name): def vgreduce(vg_name, pv_list): pvs = " ".join(pv_list) rc = iutil.execWithRedirect("lvm", ["vgreduce", vg_name, pvs], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath=1) if rc: @@ -220,7 +221,7 @@ def vginfo(vg_name): ["vgs", "--noheadings", "--nosuffix", "--units", "m", "-o", "uuid,size,free,extent_size,extent_count,free_count,pv_count", vg_name], - stderr="/dev/null") + stderr="/dev/tty5") info = buf.split() if len(info) != 7: raise LVMError(_("vginfo failed for %s" % vg_name)) @@ -235,7 +236,7 @@ def lvs(vg_name): ["lvs", "--noheadings", "--nosuffix", "--units", "m", "-o", "lv_name,lv_uuid,lv_size"], - stderr="/dev/null") + stderr="/dev/tty5") lvs = {} @@ -255,8 +256,8 @@ def lvcreate(vg_name, lv_name, size): "-L", size_arg, "-n", lv_name, vg_name], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath=1) if rc: @@ -265,8 +266,8 @@ def lvcreate(vg_name, lv_name, size): def lvremove(vg_name, lv_name): lv_path = "%s/%s" % (vg_name, lv_name) rc = iutil.execWithRedirect("lvm", ["lvremove", lv_path], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath=1) if rc: @@ -279,8 +280,8 @@ def lvresize(vg_name, lv_name, size): ["lvresize", "-L", size_arg, lv_path], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath=1) if rc: @@ -290,8 +291,8 @@ def lvactivate(vg_name, lv_name): # see if lvchange accepts paths of the form 'mapper/$vg-$lv' lv_path = "%s/%s" % (vg_name, lv_name) rc = iutil.execWithRedirect("lvm", ["lvchange", "-a", "y", lv_path], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath=1) if rc: raise LVMError("lvactivate failed for %s" % lv_path) @@ -299,8 +300,8 @@ def lvactivate(vg_name, lv_name): def lvdeactivate(vg_name, lv_name): lv_path = "%s/%s" % (vg_name, lv_name) rc = iutil.execWithRedirect("lvm", ["lvchange", "-a", "n", lv_path], - stdout = "/dev/null", - stderr = "/dev/null", + stdout = "/dev/tty5", + stderr = "/dev/tty5", searchPath=1) if rc: diff --git a/storage/devicelibs/mdraid.py b/storage/devicelibs/mdraid.py index 75613d46a..a0d2f6ae3 100644 --- a/storage/devicelibs/mdraid.py +++ b/storage/devicelibs/mdraid.py @@ -108,8 +108,8 @@ def mdcreate(device, level, disks, spares=0): rc = iutil.execWithRedirect("mdadm", argv, - stderr = "/dev/null", - stdout = "/dev/null", + stderr = "/dev/tty5", + stdout = "/dev/tty5", searchPath=1) if rc: @@ -121,23 +121,21 @@ def mdcreate(device, level, disks, spares=0): def mddestroy(device): rc = iutil.execWithRedirect("mdadm", ["--zero-superblock", device], - stderr = "/dev/null", - stdout = "/dev/null", + stderr = "/dev/tty5", + stdout = "/dev/tty5", searchPath=1) if rc: raise MDRaidError("mddestroy failed for %s" % device) def mdadd(device): - # XXX NOTUSED: mdadm -I is broken and dledford says it should be - # avoided if possible, so we used mdadm -A instead rc = iutil.execWithRedirect("mdadm", ["--incremental", "--quiet", - "--auto=yes", + "--auto=md", device], - stderr = "/dev/null", - stdout = "/dev/null", + stderr = "/dev/tty5", + stdout = "/dev/tty5", searchPath=1) if rc: @@ -174,8 +172,8 @@ def mdactivate(device, members=[], super_minor=None, uuid=None): identifier, "--auto=md", "--update=super-minor"], - stderr = "/dev/null", - stdout = "/dev/null", + stderr = "/dev/tty5", + stdout = "/dev/tty5", searchPath=1) if filename and os.access(filename, os.R_OK): @@ -191,8 +189,8 @@ def mdactivate(device, members=[], super_minor=None, uuid=None): def mddeactivate(device): rc = iutil.execWithRedirect("mdadm", ["--stop", device], - stderr = "/dev/null", - stdout = "/dev/null", + stderr = "/dev/tty5", + stdout = "/dev/tty5", searchPath=1) if rc: @@ -207,7 +205,7 @@ def mdexamine(device): # parsed output format. lines = iutil.execWithCapture("mdadm", ["--examine", device], - stderr="/dev/null").splitlines() + stderr="/dev/tty5").splitlines() info = { 'major': "-1", diff --git a/storage/devicelibs/swap.py b/storage/devicelibs/swap.py index b6c9b1ab8..8ee5b5bb3 100644 --- a/storage/devicelibs/swap.py +++ b/storage/devicelibs/swap.py @@ -20,6 +20,8 @@ # Red Hat Author(s): Dave Lehman # +import resource + import iutil from ..errors import * @@ -35,8 +37,8 @@ def mkswap(device, label=''): argv.append(device) rc = iutil.execWithRedirect("mkswap", argv, - stderr = "/dev/null", - stdout = "/dev/null", + stderr = "/dev/tty5", + stdout = "/dev/tty5", searchPath=1) if rc: @@ -68,14 +70,14 @@ def swapon(device, priority=None): raise SuspendError argv = [] - if 0 <= priority <= 32767: + if isinstance(priority, int) and 0 <= priority <= 32767: argv.extend(["-p", priority]) argv.append(device) rc = iutil.execWithRedirect("swapon", argv, - stderr = "/dev/null", - stdout = "/dev/null", + stderr = "/dev/tty5", + stdout = "/dev/tty5", searchPath=1) if rc: @@ -83,8 +85,8 @@ def swapon(device, priority=None): def swapoff(device): rc = iutil.execWithRedirect("swapoff", [device], - stderr = "/dev/null", - stdout = "/dev/null", + stderr = "/dev/tty5", + stdout = "/dev/tty5", searchPath=1) if rc: diff --git a/storage/devicetree.py b/storage/devicetree.py index 7950d31f5..b94d0990c 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -409,7 +409,7 @@ class DeviceTree(object): sysfs_path = udev_device_get_sysfs_path(info) device = None - if self.isIgnored(sysfs_path): + if self.isIgnored(info): log.debug("ignoring %s (%s)" % (name, sysfs_path)) return diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py index feb92f3dd..3025a626b 100644 --- a/storage/formats/__init__.py +++ b/storage/formats/__init__.py @@ -46,9 +46,9 @@ default_fstypes = ("ext4", "ext3", "ext2") default_boot_fstypes = ("ext3", "ext2") def get_default_filesystem_type(boot=None): if boot: - fstypes = default_boot_filesystem_types + fstypes = default_boot_fstypes else: - fstypes = default_filesystem_types + fstypes = default_fstypes for fstype in fstypes: try: @@ -136,14 +136,16 @@ class DeviceFormat(object): _type = None _name = "Unknown" _udevTypes = [] + partedFlags = None _formattable = False # can be formatted _supported = False # is supported _linuxNative = False # for clearpart _packages = [] # required packages _resizable = False # can be resized - _bootable = False # can be used as boot - _maxsize = 0 # maximum size in MB - _minsize = 0 # minimum size in MB + _bootable = False # can be used as boot + _migratable = False # can be migrated + _maxSize = 0 # maximum size in MB + _minSize = 0 # minimum size in MB _dump = False _check = False @@ -187,19 +189,22 @@ class DeviceFormat(object): return self._type def probe(self): - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) def notifyKernel(self): - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type) + if not self.device: + return + if self.device.startswith("/dev/mapper/"): try: name = dm_node_from_name(os.path.basename(self.device)) except Exception, e: log.warning("failed to get dm node for %s" % self.device) return - else: + elif self.device: name = os.path.basename(self.device) path = get_sysfs_path_by_name(name) @@ -210,18 +215,18 @@ class DeviceFormat(object): def create(self, *args, **kwargs): - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) # allow late specification of device path device = kwargs.get("device") if device: self.device = device - if not os.path.exists(device): + if not os.path.exists(self.device): raise FormatCreateError("invalid device specification") def destroy(self, *args, **kwargs): - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) # zero out the 1MB at the beginning and end of the device in the # hope that it will wipe any metadata from filesystems that @@ -237,9 +242,12 @@ class DeviceFormat(object): except Exception, e: log.error("error zeroing out %s: %s" % (self.device, e)) + self.exists = False + def setup(self, *args, **kwargs): - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) + if not self.exists: raise FormatSetupError("format has not been created") @@ -251,19 +259,21 @@ class DeviceFormat(object): if device: self.device = device - if not os.path.exists(device): + if not self.device or not os.path.exists(self.device): raise FormatSetupError("invalid device specification") def teardown(self, *args, **kwargs): - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) pass @property def status(self): - if not self.exists: - return False - return os.path.exists(self.device) + return (self.exists and + self.__class__ is not DeviceFormat and + isinstance(self.device, str) and + self.device and + os.path.exists(self.device)) @property def formattable(self): @@ -311,14 +321,14 @@ class DeviceFormat(object): return self._check @property - def maxsize(self): + def maxSize(self): """ Maximum size (in MB) for this format type. """ - return self._maxsize + return self._maxSize @property - def minsize(self): + def minSize(self): """ Minimum size (in MB) for this format type. """ - return self._minsize + return self._minSize collect_device_format_classes() diff --git a/storage/formats/dmraid.py b/storage/formats/dmraid.py index 3e1d5c3e4..d458b45c3 100644 --- a/storage/formats/dmraid.py +++ b/storage/formats/dmraid.py @@ -55,8 +55,8 @@ class DMRaidMember(DeviceFormat): _packages = ["dmraid"] # required packages _resizable = False # can be resized _bootable = False # can be used as boot - _maxsize = 0 # maximum size in MB - _minsize = 0 # minimum size in MB + _maxSize = 0 # maximum size in MB + _minSize = 0 # minimum size in MB def __init__(self, *args, **kwargs): """ Create a DeviceFormat instance. @@ -75,12 +75,12 @@ class DMRaidMember(DeviceFormat): self.raidSet = kwargs.get("raidSet") def create(self, *args, **kwargs): - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) raise DMRaidMemberError("creation of dmraid members is not supported") def destroy(self, *args, **kwargs): - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) raise DMRaidMemberError("destruction of dmraid members is not supported") diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 0359b0b6a..83b3898c1 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -32,6 +32,10 @@ import isys from ..errors import * from . import DeviceFormat, register_device_format import iutil +from flags import flags + +# is this nasty? +log_method_call = iutil.log_method_call import logging log = logging.getLogger("storage") @@ -84,8 +88,8 @@ def fsConfigFromFile(config_file): resizable = True bootable = True linuxNative = True - maxsize = 8 * 1024 * 1024 - minsize = 0 + maxSize = 8 * 1024 * 1024 + minSize = 0 defaultFormatOptions = "-t ext3" defaultMountOptions = "defaults" @@ -158,7 +162,7 @@ class FS(DeviceFormat): self._targetSize = None return - if not self.minsize < newsize < self.maxsize: + if not self.minSize < newsize < self.maxSize: raise ValueError("invalid target size request") self._targetSize = newsize @@ -205,7 +209,7 @@ class FS(DeviceFormat): if self.exists: raise FormatCreateError("filesystem already exists", self.device) - if not self.formattable(): + if not self.formattable: return if not self.mkfsProg: @@ -229,7 +233,8 @@ class FS(DeviceFormat): try: rc = iutil.execWithPulseProgress(self.mkfsProg, argv, - stderr="/dev/null", + stdout="/dev/tty5", + stderr="/dev/tty5", progress=w) except Exception as e: raise FormatCreateError(e, self.device) @@ -293,7 +298,8 @@ class FS(DeviceFormat): try: rc = iutil.execWithPulseProgress(self.resizefsProg, argv, - stderr="/dev/null", + stdout="/dev/tty5", + stderr="/dev/tty5", progress=w) except Exception as e: raise FSResizeError(e, self.device) @@ -338,8 +344,8 @@ class FS(DeviceFormat): try: rc = iutil.execWithPulseProgress(self.fsckProg, argv, - stdout="/dev/null", - stderr="/dev/null", + stdout="/dev/tty5", + stderr="/dev/tty5", progress = w) except Exception as e: raise FSError("filesystem check failed: %s" % e) @@ -413,10 +419,10 @@ class FS(DeviceFormat): ret = isys.resetFileContext(mountpoint) log.info("set SELinux context for newly mounted filesystem " "root at %s to %s" %(mountpoint, ret)) - if FS.lostAndFountContext is None: - FS.lostAndFoundContext = isys.matchPathContext("/lost+found") + if self.lostAndFoundContext is None: + self.lostAndFoundContext = isys.matchPathContext("/lost+found") isys.setFileContext("%s/lost+found" % mountpoint, - FS.lostAndFoundContext) + self.lostAndFoundContext) self._mountpoint = mountpoint @@ -458,7 +464,7 @@ class FS(DeviceFormat): argv = self._getLabelArgs(label) rc = iutil.execWithRedirect(self.labelfsProg, argv, - stderr="/dev/null", + stderr="/dev/tty5", searchPath=1) if rc: raise FSError("label failed") @@ -635,7 +641,7 @@ class FATFS(FS): _labelfs = "dosfslabel" _fsck = "dosfsck" _formattable = True - _maxsize = 1024 * 1024 + _maxSize = 1024 * 1024 _packages = [ "dosfstools" ] _defaultMountOptions = ["umask=0077", "shortname=winnt"] @@ -663,7 +669,7 @@ class BTRFS(FS): _dump = True _check = True _packages = ["btrfs-progs"] - _maxsize = 16 * 1024 * 1024 + _maxSize = 16 * 1024 * 1024 def _getFormatArgs(self, options=None): argv = [] @@ -713,7 +719,7 @@ class JFS(FS): _defaultFormatOptions = ["-q"] _defaultLabelOptions = ["-L"] _maxLabelChars = 16 - _maxsize = 8 * 1024 * 1024 + _maxSize = 8 * 1024 * 1024 _formattable = True _linuxNative = True _supported = False @@ -731,7 +737,7 @@ class XFS(FS): _defaultFormatOptions = ["-f"] _defaultLabelOptions = ["-L"] _maxLabelChars = 16 - _maxsize = 16 * 1024 * 1024 + _maxSize = 16 * 1024 * 1024 _formattable = True _linuxNative = True _supported = False @@ -763,28 +769,35 @@ class NTFS(FS): _type = "ntfs" _resizefs = "ntfsresize" _resizable = True + _minSize = 1 _defaultMountOptions = "defaults" #_packages = ["ntfsprogs"] - def getMinimumSize(self): - """Return the minimum filesystem size in megabytes""" - if not self.exists: - raise FSError("filesystem has not been created") - - buf = iutil.execWithCapture(self.resizefsProg, - ["-m", self.device], - stderr = "/dev/tty5") - for l in buf.split("\n"): - if not l.startswith("Minsize"): - continue - try: - min = l.split(":")[1].strip() - return int(min) + 250 - except Exception, e: - log.warning("Unable to parse output for minimum size on %s: %s" %(self.device, e)) - - log.warning("Unable to discover minimum size of filesystem on %s" %(self.device,)) - return 1 + def minSize(self): + """ The minimum filesystem size in megabytes. """ + size = self._minSize + if self.exists: + minSize = None + buf = iutil.execWithCapture(self.resizefsProg, + ["-m", self.device], + stderr = "/dev/tty5") + for l in buf.split("\n"): + if not l.startswith("Minsize"): + continue + try: + min = l.split(":")[1].strip() + minSize = int(min) + 250 + except Exception, e: + minSize = None + log.warning("Unable to parse output for minimum size on %s: %s" %(self.device, e)) + + if minSize is None: + log.warning("Unable to discover minimum size of filesystem " + "on %s" %(self.device,)) + else: + size = minSize + + return size register_device_format(NTFS) diff --git a/storage/formats/luks.py b/storage/formats/luks.py index 607b44bd9..a2b3b62d3 100644 --- a/storage/formats/luks.py +++ b/storage/formats/luks.py @@ -107,11 +107,14 @@ class LUKS(DeviceFormat): def setup(self, *args, **kwargs): """ Open, or set up, the format. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.configured: raise LUKSError("luks device not configured") + if self.status: + return + DeviceFormat.setup(self, *args, **kwargs) crypto.luks_open(self.device, self.mapName, passphrase=self.__passphrase, @@ -119,7 +122,7 @@ class LUKS(DeviceFormat): def teardown(self, *args, **kwargs): """ Close, or tear down, the format. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.exists: raise LUKSError("format has not been created") @@ -130,7 +133,7 @@ class LUKS(DeviceFormat): def create(self, *args, **kwargs): """ Create the format. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.configured: raise LUKSError("luks device not configured") @@ -143,6 +146,7 @@ class LUKS(DeviceFormat): key_size=self.key_size) self.uuid = crypto.luks_uuid(self.device) + self.exists = True self.notifyKernel() @property @@ -156,7 +160,7 @@ class LUKS(DeviceFormat): Add the contents of the specified key file to an available key slot in the LUKS header. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status, file=keyfile) if not self.exists: raise LUKSError("format has not been created") @@ -172,7 +176,7 @@ class LUKS(DeviceFormat): Add the specified passphrase to an available key slot in the LUKS header. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.exists: raise LUKSError("format has not been created") @@ -188,7 +192,7 @@ class LUKS(DeviceFormat): Remove key contained in the specified key file from the LUKS header. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status, file=keyfile) if not self.exists: raise LUKSError("format has not been created") @@ -201,7 +205,7 @@ class LUKS(DeviceFormat): def removePassphrase(self, passphrase): """ Remove the specified passphrase from the LUKS header. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.exists: raise LUKSError("format has not been created") diff --git a/storage/formats/lvmpv.py b/storage/formats/lvmpv.py index d27819859..e5dfa5837 100644 --- a/storage/formats/lvmpv.py +++ b/storage/formats/lvmpv.py @@ -20,6 +20,8 @@ # Red Hat Author(s): Dave Lehman # +import os + from iutil import log_method_call from parted import PARTITION_LVM from ..errors import * @@ -67,7 +69,7 @@ class LVMPhysicalVolume(DeviceFormat): def probe(self): """ Probe for any missing information about this device. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.exists: raise PhysicalVolumeError("format has not been created") @@ -79,18 +81,19 @@ class LVMPhysicalVolume(DeviceFormat): def create(self, *args, **kwargs): """ Create the format. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) DeviceFormat.create(self, *args, **kwargs) """ Consider use of -Z|--zero -f|--force or -y|--yes may be required """ lvm.pvcreate(self.device) + self.exists = True self.notifyKernel() def destroy(self, *args, **kwargs): """ Destroy the format. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.exists: raise PhysicalVolumeError("format has not been created") @@ -100,16 +103,14 @@ class LVMPhysicalVolume(DeviceFormat): # FIXME: verify path exists? lvm.pvremove(self.device) + self.exists = False self.notifyKernel() @property def status(self): # XXX hack - for d in os.listdir("/dev/mapper"): - if d.startswith("%s-" % self.vgName): - return True - return False - + return (self.exists and self.vgName and + os.path.isdir("/dev/mapper/%s" % self.vgName)) register_device_format(LVMPhysicalVolume) diff --git a/storage/formats/mdraid.py b/storage/formats/mdraid.py index 90a631c18..fde4852d7 100644 --- a/storage/formats/mdraid.py +++ b/storage/formats/mdraid.py @@ -66,7 +66,7 @@ class MDRaidMember(DeviceFormat): def probe(self): """ Probe for any missing information about this format. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.exists: raise MDRaidMemberError("format does not exist") @@ -81,10 +81,11 @@ class MDRaidMember(DeviceFormat): if not self.exists: raise MDMemberError("format does not exist") - if not os.path.access(self.device, os.W_OK): + if not os.access(self.device, os.W_OK): raise MDMemberError("device path does not exist") - mdadm.mddestroy(self.device) + mdraid.mddestroy(self.device) + self.exists = False @property def status(self): diff --git a/storage/formats/swap.py b/storage/formats/swap.py index 6b8646d28..5f5958383 100644 --- a/storage/formats/swap.py +++ b/storage/formats/swap.py @@ -86,8 +86,13 @@ class SwapSpace(DeviceFormat): return opts def _setOptions(self, opts): - for (opt, arg) in opts.split(","): - if opt == "pri": + if not opts: + self.priority = None + return + + for option in opts.split(","): + (opt, equals, arg) = option.partition("=") + if equals and opt == "pri": self.priority = numeric_type(arg) options = property(_getOptions, _setOptions, @@ -100,7 +105,7 @@ class SwapSpace(DeviceFormat): def setup(self, *args, **kwargs): """ Open, or set up, a device. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.exists: raise SwapSpaceError("format has not been created") @@ -113,7 +118,7 @@ class SwapSpace(DeviceFormat): def teardown(self, *args, **kwargs): """ Close, or tear down, a device. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.exists: raise SwapSpaceError("format has not been created") @@ -123,7 +128,7 @@ class SwapSpace(DeviceFormat): def create(self, *args, **kwargs): """ Create the device. """ - log_method_call(self, device=os.path.basename(self.device), + log_method_call(self, device=self.device, type=self.type, status=self.status) if self.exists: raise SwapSpaceError("format already exists") @@ -133,6 +138,7 @@ class SwapSpace(DeviceFormat): DeviceFormat.create(self, *args, **kwargs) swap.mkswap(self.device, label=self.label) + self.exists = True register_device_format(SwapSpace) diff --git a/storage/udev.py b/storage/udev.py index 05fa65c9b..4f83c2ad3 100644 --- a/storage/udev.py +++ b/storage/udev.py @@ -53,7 +53,6 @@ def enumerate_block_devices(): return devices def udev_get_block_device(sysfs_path): - log.debug("getting udev info for %s" % sysfs_path) if not os.path.exists(sysfs_path): log.debug("%s does not exist" % sysfs_path) return None diff --git a/textw/upgrade_text.py b/textw/upgrade_text.py index 65cbaeb38..54c36edcb 100644 --- a/textw/upgrade_text.py +++ b/textw/upgrade_text.py @@ -190,7 +190,7 @@ class UpgradeSwapWindow: else: screen.popWindow() if flags.setupFilesystems: - upgrade.createSwapFile(anaconda.rootPath, dev, val) + anaconda.id.storage.fsset.createSwapFile(anaconda.rootPath, dev, val) anaconda.dispatch.skipStep("addswap", 1) return INSTALL_OK diff --git a/upgrade.py b/upgrade.py index f8e299626..c7e8efd5f 100644 --- a/upgrade.py +++ b/upgrade.py @@ -31,7 +31,8 @@ import selinux from flags import flags from constants import * from product import productName -from storage import findExistingRootDevices, FSSet +from storage import findExistingRootDevices +from storage import mountExistingSystem from storage.formats import getFormat import rhpl @@ -134,7 +135,7 @@ def findRootParts(anaconda): anaconda.id.upgradeRoot = [] for (dev, label) in anaconda.id.rootParts: - anaconda.id.upgradeRoot.append( (dev, fs) ) + anaconda.id.upgradeRoot.append( (dev, label) ) if anaconda.id.rootParts is not None and len(anaconda.id.rootParts) > 0: anaconda.dispatch.skipStep("findinstall", skip = 0) @@ -151,6 +152,7 @@ def findExistingRoots(anaconda, upgradeany = 0): return [(anaconda.rootPath, "")] return [] + rootparts = findExistingRootDevices(anaconda, upgradeany=upgradeany) return rootparts def bindMountDevDirectory(instPath): @@ -161,7 +163,7 @@ def bindMountDevDirectory(instPath): # returns None if no filesystem exist to migrate def upgradeMigrateFind(anaconda): - migents = anaconda.id.fsset.getMigratableEntries() + migents = anaconda.id.storage.fsset.getMigratableEntries() if not migents or len(migents) < 1: anaconda.dispatch.skipStep("upgrademigratefs") else: @@ -199,7 +201,7 @@ def upgradeSwapSuggestion(anaconda): fsList = [] - for device in anaconda.id.fsset.devices: + for device in anaconda.id.storage.fsset.devices: if not device.format: continue if device.format.mountable and device.format.linuxNative: diff --git a/yuminstall.py b/yuminstall.py index 490c97e0b..fd859d84e 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -758,7 +758,7 @@ class AnacondaYum(YumSorter): if len(mkeys) > 1: stage2img = "%s/images/install.img" % self.tree if self.anaconda.backend.mountInstallImage(self.anaconda, stage2img): - self.anaconda.id.fsset.unmountFilesystems(self.anaconda.rootPath) + self.anaconda.id.storage.fsset.unmountFilesystems(self.anaconda.rootPath) return DISPATCH_BACK for i in mkeys: @@ -1498,11 +1498,11 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon log.warning("no dev package, going to bind mount /dev") isys.mount("/dev", "%s/dev" %(anaconda.rootPath,), bindMount = 1) if not upgrade: - anaconda.id.fsset.mkDevRoot(anaconda.rootPath) + anaconda.id.storage.fsset.mkDevRoot(anaconda.rootPath) # write out the fstab if not upgrade: - anaconda.id.fsset.write(anaconda.rootPath) + anaconda.id.storage.fsset.write(anaconda.rootPath) # rootpath mode doesn't have this file around if os.access("/etc/modprobe.d/anaconda", os.R_OK): shutil.copyfile("/etc/modprobe.d/anaconda", @@ -1515,7 +1515,7 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon # make a /etc/mtab so mkinitrd can handle certain hw (usb) correctly f = open(anaconda.rootPath + "/etc/mtab", "w+") - f.write(anaconda.id.fsset.mtab()) + f.write(anaconda.id.storage.fsset.mtab()) f.close() def checkSupportedUpgrade(self, anaconda): -- cgit From d6b083f08ca85e8c354a53127511dfe2f4ddedaa Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 11:06:04 -0600 Subject: Various changes, including several fixes to device discovery. Allow redundant stacking of actions caused by repeated fiddling via partitioning dialogs. We can implement simple loop detection. Add a method to find actions based on various criteria. Don't try to start md arrays after adding devices as that is done automatically by mdadm. The list of devices we're about to scan is worthy of info logging IMO. --- storage/devicetree.py | 91 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 57 insertions(+), 34 deletions(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index b94d0990c..ff95568e2 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -25,7 +25,7 @@ import os from errors import * from devices import * from deviceaction import * -from formats import * +import formats from udev import * import gettext @@ -61,7 +61,7 @@ def getLUKSPassphrase(intf, device, globalPassphrase): # the device is already mapped raise RuntimeError("device is already mapped") - if not device.format.configured and passphrase: + if not device.format.configured and globalPassphrase: # try the given passphrase first device.format.passphrase = globalPassphrase @@ -74,6 +74,7 @@ def getLUKSPassphrase(intf, device, globalPassphrase): return (globalPassphrase, False) buttons = [_("Back"), _("Continue")] + passphrase_incorrect = False while True: if passphrase_incorrect: # TODO: add a flag to passphraseEntryWindow to say the last @@ -100,6 +101,8 @@ def getLUKSPassphrase(intf, device, globalPassphrase): log.info("skipping passphrase for %s" % (device.name,)) break + device.format.passphrase = passphrase + try: device.format.setup() except CryptoError as e: @@ -263,6 +266,7 @@ class DeviceTree(object): log.info("executing action: %s" % action) if not dryRun: action.execute(intf=self.intf) + udev_settle(timeout=10) def _addDevice(self, newdev): """ Add a device to the tree. @@ -313,38 +317,29 @@ class DeviceTree(object): Modifications to the Device instance are handled before we get here. """ - removedAction = None - for _action in self._actions: - if _action.device == action.device and \ - _action.type == action.type and \ - _action.obj == action.obj: - #raise DeviceTreeError("duplicate action for this device") - log.debug("cancelling action '%s' in favor of '%s'" % (_action, - action)) - self.cancelAction(_action) - removedAction = _action - break - if (action.isDestroy() or action.isResize() or \ (action.isCreate() and action.isFormat())) and \ action.device not in self._devices: raise DeviceTreeError("device is not in the tree") elif (action.isCreate() and action.isDevice()) and \ - action.device in self._devices: - raise DeviceTreeError("device is already in the tree") + (action.device in self._devices or \ + action.device.path in [d.path for d in self._devices]): + # this allows multiple create actions w/o destroy in between; + # we will clean it up before processing actions + #raise DeviceTreeError("device is already in the tree") + self._removeDevice(action.device) if action.isCreate() and action.isDevice(): self._addDevice(action.device) elif action.isDestroy() and action.isDevice(): self._removeDevice(action.device) elif action.isCreate() and action.isFormat(): - if isinstance(action.device.format, FS) and \ + if isinstance(action.device.format, formats.fs.FS) and \ action.device.format.mountpoint in self.filesystems: raise DeviceTreeError("mountpoint already in use") log.debug("registered action: %s" % action) self._actions.append(action) - return removedAction def cancelAction(self, action): """ Cancel a registered action. @@ -362,6 +357,38 @@ class DeviceTree(object): # add the device back into the tree self._addDevice(action.device) + def findActions(self, device=None, type=None, object=None): + """ Find all actions that match all specified parameters. + + Keyword arguments: + + device -- device to match (Device, or None to match any) + type -- action type to match (string, or None to match any) + object -- operand type to match (string, or None to match any) + + """ + if device is None and type is None and object is None: + return self._actions[:] + + # convert the string arguments to the types used in actions + _type = action_type_from_string(type) + _object = action_object_from_string(object) + + actions = [] + for action in self._actions: + if device is not None and action.device != device: + continue + + if _type is not None and action.type != _type: + continue + + if _object is not None and action.obj != _object: + continue + + actions.append(action) + + return actions + def getDependentDevices(self, dep): """ Return a list of devices that depend on dep. @@ -584,17 +611,14 @@ class DeviceTree(object): format = None format_type = udev_device_get_format(info) label = udev_device_get_label(info) - if device and format_type and not device.format: + if device and format_type and not device.format.type: args = [format_type] kwargs = {"uuid": uuid, "label": label, "device": device.path, "exists": True} - if format_type == "swap": - # swap - pass - elif format_type == "crypto_LUKS": + if format_type == "crypto_LUKS": # luks/dmcrypt kwargs["mapName"] = "luks-%s" % uuid elif format_type == "linux_raid_member": @@ -614,7 +638,8 @@ class DeviceTree(object): kwargs["vgUuid"] = udev_device_get_vg_uuid(info) kwargs["peStart"] = udev_device_get_pv_pe_start(info) - device.format = getFormat(*args, **kwargs) + format = formats.getFormat(*args, **kwargs) + device.format = format # # now lookup or create any compound devices we have discovered @@ -637,16 +662,18 @@ class DeviceTree(object): if isglobal and format.status: self.__passphrase = passphrase - luks_device = LUKSDevice(device.format.MapName, + luks_device = LUKSDevice(device.format.mapName, parents=[device], exists=True) self._addDevice(luks_device) try: luks_device.setup() - except DeviceError as e: - log.info("setup of %s failed: %s" % (map_name, e)) + except (LUKSError, CryptoError, DeviceError) as e: + log.info("setup of %s failed: %s" % (format.mapName, + e)) else: - log.warning("luks device %s already in the tree" % map_name) + log.warning("luks device %s already in the tree" + % format.mapName) elif format.type == "mdmember": # either look up or create the array device md_array = self.getDeviceByUuid(format.mdUuid) @@ -681,15 +708,11 @@ class DeviceTree(object): exists=True, parents=[device]) self._addDevice(md_array) - try: - md_array.setup() - except DeviceError as e: - log.info("setup of %s failed: %s" % (md_array.name, e)) elif format.type == "dmraidmember": # look up or create the dmraid array pass elif format.type == "lvmpv": - # lookup/create the VG and LVsA + # lookup/create the VG and LVs try: vg_name = udev_device_get_vg_name(info) except KeyError: @@ -780,7 +803,7 @@ class DeviceTree(object): break old_devices = new_devices - log.debug("devices to scan: %s" % [d['name'] for d in devices]) + log.info("devices to scan: %s" % [d['name'] for d in devices]) for dev in devices: self.addUdevDevice(dev) -- cgit From e0071db60c898e310ee4f9e8d9cb5606ed561565 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 11:20:11 -0600 Subject: Various changes to format base class and fs base class. - Add migratable property to all formats to indicate whether a format can be migrated. - Add migrate property to all formats to indicate whether a format is being migrated. - Make options a property of all formats and have FS classes return their mount options as their options value. - Add FS.doMigrate. - Rename all methods that do stuff in FS from foo to doFoo to improve clarity. - Replace FS getMinimumSize method with minSize property. --- storage/formats/__init__.py | 22 ++++++ storage/formats/fs.py | 179 ++++++++++++++++++++++++++++++++++---------- 2 files changed, 163 insertions(+), 38 deletions(-) diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py index 3025a626b..13ed3d61f 100644 --- a/storage/formats/__init__.py +++ b/storage/formats/__init__.py @@ -163,6 +163,19 @@ class DeviceFormat(object): self.uuid = kwargs.get("uuid") self.exists = kwargs.get("exists") self.options = kwargs.get("options") + self._migrate = False + + # don't worry about existence if this is a DeviceFormat instance + #if self.__class__ is DeviceFormat: + # self.exists = True + + def _setOptions(self, options): + self._options = options + + def _getOptions(self, options): + return self._options + + options = property(_getOptions, _setOptions) def _setDevice(self, devspec): if devspec and not devspec.startswith("/"): @@ -300,6 +313,15 @@ class DeviceFormat(object): """ Is this format type suitable for a boot partition? """ return self._bootable + @property + def migratable(self): + """ Can formats of this type be migrated? """ + return self_migratable + + @property + def migrate(self): + return self._migrate + @property def linuxNative(self): """ Is this format type native to linux? """ diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 83b3898c1..511ac3c01 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -113,16 +113,19 @@ def fsConfigFromFile(config_file): class FS(DeviceFormat): """ Filesystem class. """ - _type = "filesystem" # fs type - _name = "Abstract Filesystem Class" # fs type name + _type = "Abstract Filesystem Class" # fs type name + _name = None _mkfs = "" # mkfs utility _resizefs = "" # resize utility _labelfs = "" # labeling utility _fsck = "" # fs check utility + _migratefs = "" # fs migration utility _defaultFormatOptions = [] # default options passed to mkfs _defaultMountOptions = ["defaults"] # default options passed to mount _defaultLabelOptions = [] _defaultCheckOptions = [] + _defaultMigrateOptions = [] + _migrationTarget = None lostAndFoundContext = None def __init__(self, *args, **kwargs): @@ -149,7 +152,7 @@ class FS(DeviceFormat): self.label = kwargs.get("label") # filesystem size does not necessarily equal device size self._size = kwargs.get("size") - self._targetSize = None + self._targetSize = self._size self._mountpoint = None # the current mountpoint when mounted def _setTargetSize(self, newsize): @@ -176,9 +179,21 @@ class FS(DeviceFormat): def _getSize(self): """ Get this filesystem's size. """ - return self._size + size = self._size + if self.resizable and self.targetSize != size: + size = self.targetSize + return size - size = property(_getSize, doc="This filesystem's size") + size = property(_getSize, doc="This filesystem's size, accounting " + "for pending changes") + + @property + def currentSize(self): + """ The filesystem's current actual size. """ + size = 0 + if self.exists: + size = self._size + return size def _getFormatArgs(self, options=None): argv = [] @@ -187,7 +202,7 @@ class FS(DeviceFormat): argv.append(self.device) return argv - def format(self, *args, **kwargs): + def doFormat(self, *args, **kwargs): """ Create the filesystem. Arguments: @@ -248,11 +263,47 @@ class FS(DeviceFormat): self.exists = True self.notifyKernel() + def doMigrate(self, intf=None): + if not self.exists: + raise FSError("filesystem has not been created") + + if not self.migratable or not self.migrate: + return + + if not os.path.exists(self.device): + raise FSError("device does not exist") + + # if journal already exists skip + if isys.ext2HasJournal(self.device): + log.info("Skipping migration of %s, has a journal already." + % self.device) + return + + argv = self._defaultMigrateOptions[:] + argv.append(self.device) + try: + rc = iutil.execWithRedirect(self.migratefsProg, + argv, + stdout = "/dev/tty5", + stderr = "/dev/tty5", + searchPath = 1) + except Exception as e: + raise FSMigrateError("filesystem migration failed: %s" % e, + self.device) + + if rc: + raise FSMigrateError("filesystem migration failed: %s" % rc, + self.device) + + # the other option is to actually replace this instance with an + # instance of the new filesystem type. + self._type = self.migrationTarget + def _getResizeArgs(self): argv = [self.device, self.targetSize] return argv - def resize(self, *args, **kwargs): + def doResize(self, *args, **kwargs): """ Resize this filesystem to new size @newsize. Arguments: @@ -273,9 +324,7 @@ class FS(DeviceFormat): # should this instead raise an exception? return - if self.targetSize is None: - # FIXME: implement minimum size method/attr and/or checking in - # setter for targetSize property + if self.targetSize == self.currentSize: return if not self.resizefsProg: @@ -284,7 +333,7 @@ class FS(DeviceFormat): if not os.path.exists(self.device): raise FSResizeError("device does not exist", self.device) - self.check(intf=intf) + self.doCheck(intf=intf) argv = self._getResizeArgs() @@ -314,17 +363,12 @@ class FS(DeviceFormat): self._size = self.targetSize self.notifyKernel() - def getMinimumSize(self): - raise NotImplementedError("getMinimumSize") - if not self.exists: - raise FSError("filesystem does not exist") - def _getCheckArgs(self): argv = [] argv.extend(self.defaultCheckOptions) argv.append(self.device) - def check(self, intf=None): + def doCheck(self, intf=None): if not self.exists: raise FSError("filesystem has not been created") @@ -402,7 +446,7 @@ class FS(DeviceFormat): # passed in options override default options if not options or not isinstance(options, str): - options = ",".join(self.defaultMountOptions) + options = self.options try: rc = isys.mount(self.device, mountpoint, @@ -491,6 +535,15 @@ class FS(DeviceFormat): """ Program used to manage labels for this filesystem type. """ return self._labelfs + @property + def migratefsProg(self): + """ Program used to migrate filesystems of this type. """ + return self._migratefs + + @property + def migrationTarget(self): + return self._migrationTarget + def supported(self): # we aren't checking for fsck because we shouldn't need it for prog in [self.mkfsProg, self.resizefsProg, self.labelfsProg]: @@ -531,6 +584,47 @@ class FS(DeviceFormat): # return a copy to prevent modification return self._defaultCheckOptions[:] + def _getOptions(self): + options = ",".join(self.defaultMountOptions) + if self.mountopts: + # XXX should we clobber or append? + options = self.mountopts + return options + + def _setOptions(self, options): + self.mountopts = options + + options = property(_getOptions, _setOptions) + + @property + def migratable(self): + """ Can filesystems of this type be migrated? """ + return (self._migratable and self.migratefsProg and + filter(lambda d: os.access("%s/%s" % (d, self.migratefsProg), + os.X_OK), + os.environ["PATH"].split(":")) and + self.migrationTarget) + + def _setMigrate(self, migrate): + if not migrate: + self._migrate = migrate + return + + if self.migratable and self.exists: + self._migrate = migrate + else: + raise ValueError("cannot set migrate on non-migratable filesystem") + + migrate = property(lambda f: f._migrate, lambda f,m: f._setMigrate(m)) + + @property + def type(self): + _type = self._type + if self.migrate: + _type = self.migrationTarget + + return _type + """ These methods just wrap filesystem-specific methods in more generically named methods so filesystems and formatted devices like swap and LVM physical volumes can have a common API. @@ -541,7 +635,7 @@ class FS(DeviceFormat): DeviceFormat.create(self, *args, **kwargs) - return self.format(*args, **kwargs) + return self.doFormat(*args, **kwargs) def setup(self, *args, **kwargs): """ Mount the filesystem. @@ -575,36 +669,42 @@ class Ext2FS(FS): _resizable = True _bootable = True _linuxNative = True - _maxsize = 8 * 1024 * 1024 - _minsize = 0 + _maxSize = 8 * 1024 * 1024 + _minSize = 0 _defaultFormatOptions = [] _defaultMountOptions = ["defaults"] _defaultCheckOptions = ["-f", "-p", "-C", "0"] _dump = True _check = True + _migratable = True + _migrationTarget = "ext3" + _migratefs = "tune2fs" + _defaultMigrateOptions = ["-j"] - def getMinimumSize(self): - if not self.exists: - raise FSError("filesystem has not been created") + @property + def minSize(self): + """ Minimum size for this filesystem in MB. """ + size = self._minSize + if self.exists: + if not os.path.exists(self.device): + raise FSError("device does not exist") - if not os.path.exists(self.device): - raise FSError("device does not exist") + buf = iutil.execWithCapture(self.resizefsProg, + ["-P", self.device], + stderr="/dev/tty5") + size = None + for line in buf.splitlines(): + if "minimum size of the filesystem:" not in line: + continue - buf = iutil.execWithCapture(self.resizefsProg, - ["-P", self.device], - stderr="/dev/null") - minSize = None - for line in buf.splitlines(): - if "minimum size of the filesystem:" not in line: - continue + (text, sep, minSize) = line.partition(": ") - (text, sep, minSize) = line.partition(": ") - minSize = int(minSize) + size = int(minSize) / 1024.0 - if minSize is None: - raise FSError("failed to get minimum fs size") + if size is None: + raise FSError("failed to get minimum fs size") - return minSize + return size @property def isDirty(self): @@ -617,6 +717,8 @@ class Ext3FS(Ext2FS): """ ext3 filesystem. """ _type = "ext3" _defaultFormatOptions = ["-t", "ext3"] + _migrationTarget = "ext4" + _defaultMigrateOptions = ["-O", "extents"] register_device_format(Ext3FS) @@ -626,6 +728,7 @@ class Ext4FS(Ext3FS): _type = "ext4" _bootable = False _defaultFormatOptions = ["-t", "ext4"] + _migratable = False register_device_format(Ext4FS) -- cgit From aa3db4ff84c65bd74f93dc751a81cac5c83364e6 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 11:28:48 -0600 Subject: Add DeviceResizeError and PartitioningWarning exceptions. --- storage/errors.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/storage/errors.py b/storage/errors.py index 3521873d3..692e0acc9 100644 --- a/storage/errors.py +++ b/storage/errors.py @@ -9,6 +9,9 @@ class DeviceCreateError(DeviceError): class DeviceDestroyError(DeviceError): pass +class DeviceResizeError(DeviceError): + pass + class DeviceSetupError(DeviceError): pass @@ -92,6 +95,9 @@ class DeviceActionError(Exception): class PartitioningError(Exception): pass +class PartitioningWarning(Exception): + pass + # udev class UdevError(Exception): pass -- cgit From 1a5146ec83e58626f197562a8f35e42e65b0a0d2 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 11:30:46 -0600 Subject: Add type lookup functions, migrate action, pass intf thru execute. ActionMigrateFormat handles filesystem migrations. Update resize actions to use currentSize and targetSize properties of devices and formats. Allow ActionCreateFormat with no format to imply the creation of the format already specified by the device. --- storage/deviceaction.py | 103 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 74 insertions(+), 29 deletions(-) diff --git a/storage/deviceaction.py b/storage/deviceaction.py index 3be35a14c..2f14a32b9 100644 --- a/storage/deviceaction.py +++ b/storage/deviceaction.py @@ -20,10 +20,9 @@ # # Red Hat Author(s): Dave Lehman # -""" action.py -""" -from devices import StorageDevice +from devices import StorageDevice, PartitionDevice +from formats import getFormat from errors import * import gettext @@ -63,6 +62,32 @@ RESIZE_GROW = 89 resize_strings = {RESIZE_SHRINK: "Shrink", RESIZE_GROW: "Grow"} +def action_type_from_string(type_string): + if type_string is None: + return None + + for (k,v) in action_strings.items(): + if v.lower() == type_string.lower(): + return k + + return resize_type_from_string(type_string) + +def action_object_from_string(type_string): + if type_string is None: + return None + + for (k,v) in object_strings.items(): + if v.lower() == type_string.lower(): + return k + +def resize_type_from_string(type_string): + if type_string is None: + return None + + for (k,v) in resize_strings.items(): + if v.lower() == type_string.lower(): + return k + class DeviceAction(object): """ An action that will be carried out in the future on a Device. @@ -116,7 +141,7 @@ class DeviceAction(object): self.device = device #self._backup = deepcopy(device) - def execute(self): + def execute(self, intf=None): """ perform the action """ pass @@ -172,8 +197,8 @@ class ActionCreateDevice(DeviceAction): # FIXME: assert device.fs is None DeviceAction.__init__(self, device) - def execute(self): - self.device.create() + def execute(self, intf=None): + self.device.create(intf=intf) class ActionDestroyDevice(DeviceAction): @@ -185,7 +210,7 @@ class ActionDestroyDevice(DeviceAction): # XXX should we insist that device.fs be None? DeviceAction.__init__(self, device) - def execute(self): + def execute(self, intf=None): self.device.destroy() @@ -199,18 +224,18 @@ class ActionResizeDevice(DeviceAction): raise ValueError("new size same as old size") DeviceAction.__init__(self, device) - if newsize > device.size: + if newsize > device.currentSize: self.dir = RESIZE_GROW else: self.dir = RESIZE_SHRINK - self.origsize = device.size - self.device.size = newsize + self.origsize = device.targetSize + self.device.targetSize = newsize - def execute(self): - self.device.resize() + def execute(self, intf=None): + self.device.resize(intf=intf) def cancel(self): - self.device.size = self.origsize + self.device.targetSize = self.origsize class ActionCreateFormat(DeviceAction): @@ -218,19 +243,21 @@ class ActionCreateFormat(DeviceAction): type = ACTION_TYPE_CREATE obj = ACTION_OBJECT_FORMAT - def __init__(self, device, format): + def __init__(self, device, format=None): DeviceAction.__init__(self, device) - # should we insist that device.format be None? - # (indicative of ActionDestroyFormat having been instantiated) - self.origFormat = device.format - self.device.teardownFormat() - self.device.format = format + if format: + self.origFormat = device.format + self.device.format.teardown() + self.device.format = format + else: + self.origFormat = getFormat(None) - def execute(self): + def execute(self, intf=None): # XXX we should set partition type flag as needed # - or should that be in the CreateDevice action? self.device.setup() - self.device.format.create(device=self.device.path, + self.device.format.create(intf=intf, + device=self.device.path, options=self.device.formatArgs) def cancel(self): @@ -256,9 +283,8 @@ class ActionDestroyFormat(DeviceAction): device.format.teardown() self.device.format = None - def execute(self): + def execute(self, intf=None): """ wipe the filesystem signature from the device """ - #self.device.destroyFormat() if self.origFormat: self.device.setup() self.origFormat.destroy() @@ -281,21 +307,40 @@ class ActionResizeFormat(DeviceAction): obj = ACTION_OBJECT_FORMAT def __init__(self, device, newsize): - if device.size == newsize: + if device.targetSize == newsize: raise ValueError("new size same as old size") DeviceAction.__init__(self, device) - if newsize > device.format.size: + if newsize > device.format.currentSize: self.dir = RESIZE_GROW else: self.dir = RESIZE_SHRINK - self.device.format.setSize(newsize) + self.origSize = self.device.format.targetSize + self.device.format.targetSize = newsize + + def execute(self, intf=None): + self.device.format.doResize(intf=intf) + + def cancel(self): + self.device.format.targetSize = self.origSize + +class ActionMigrateFormat(DeviceAction): + """ An action representing the migration of an existing filesystem. """ + type = ACTION_TYPE_MIGRATE + obj = ACTION_OBJECT_FORMAT + + def __init__(self, device): + if not device.format.migratable or not device.format.exists: + raise ValueError("device format is not migratable") + + DeviceAction.__init__(self, device) + self.device.format.migrate = True - def execute(self): - self.device.format.resize() + def execute(self, intf=None): + self.device.format.doMigrate(intf=intf) def cancel(self): - self.device.format.setSize(None) + self.device.format.migrate = False -- cgit From 595a18a27a0fde86be57ee601a8fcfc73005248b Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 11:39:06 -0600 Subject: Add FSSet.mountpoints, a property providing a mountpoint->device dict. --- storage/__init__.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/storage/__init__.py b/storage/__init__.py index e521723b5..45e745b6d 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -863,6 +863,13 @@ class FSSet(object): devices.sort(key=lambda d: d.path) return devices + @property + def mountpoints(self): + filesystems = {} + for device in self.devices: + if device.format.mountable and device.format.mountpoint: + filesystems[device.format.mountpoint] = device + def parseFSTab(self, chroot=""): """ parse /etc/fstab -- cgit From d2d5b627646a743e8cf158d95a28e599c29412e9 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 11:54:15 -0600 Subject: Various fixes and addiions to Storage, FSSet, getReleaseString. Storage: - get the default boot fstype when we get the regular one. - Get set up minimally in __init__. - Put up a waitWindow during scan. - Don't clobber autopart requests on reset as they're abstract. - add isProtected method. FSSet: - implement migratableDevices property. Misc: - rewrite getReleaseString. --- storage/__init__.py | 80 ++++++++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 45e745b6d..f30326520 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -98,7 +98,7 @@ class Storage(object): self.ignoredDisks = [] self.exclusiveDisks = [] self.doAutoPart = False - self._clearPartType = CLEARPART_TYPE_NONE + self.clearPartType = CLEARPART_TYPE_NONE self.clearPartDisks = [] self.encryptedAutoPart = False self.encryptionPassphrase = None @@ -115,6 +115,18 @@ class Storage(object): self._nextID = 0 self.defaultFSType = get_default_filesystem_type() + self.defaultBootFSType = get_default_filesystem_type(boot=True) + + self.devicetree = DeviceTree(intf=self.anaconda.intf, + ignored=self.ignoredDisks, + exclusive=self.exclusiveDisks, + zeroMbr=self.zeroMbr, + passphrase=self.encryptionPassphrase, + luksDict=self.__luksDevs) + self.fsset = FSSet(self.devicetree) + + def doIt(self): + self.devicetree.processActions() @property def nextID(self): @@ -143,16 +155,19 @@ class Storage(object): for device in self.devices: if device.format.type == "luks" and device.format.exists: self.__luksDevs[device.format.uuid] = device.format.__passphrase + + w = self.anaconda.intf.waitWindow(_("Finding Devices"), + _("Finding storage devices...")) self.iscsi.startup(self.anaconda.intf) self.zfcp.startup() self.devicetree = DeviceTree(intf=self.anaconda.intf, ignored=self.ignoredDisks, exclusive=self.exclusiveDisks, zeroMbr=self.zeroMbr, - passphrase=self.encrpytionPassphrase, - luksDict=self._luksDevs) + passphrase=self.encryptionPassphrase, + luksDict=self.__luksDevs) self.fsset = FSSet(self.devicetree) - self.autoPartitionRequests = [] + w.pop() @property def devices(self): @@ -445,6 +460,8 @@ class Storage(object): """ action = ActionCreateDevice(device) self.devicetree.registerAction(action) + if device.format.type: + self.devicetree.registerAction(ActionCreateFormat(device)) def destroyDevice(self, device): """ Schedule destruction of a device. """ @@ -545,6 +562,10 @@ class Storage(object): log.warning("storage.Storage.sanityCheck is unimplemented") return ([], []) + def isProtected(self, device): + """ Return True is the device is protected. """ + return device.name in self.protectedPartitions + def checkNoDisks(self): """Check that there are valid disk devices.""" if not self.disks: @@ -561,43 +582,21 @@ def getReleaseString(mountpoint): relName = None relVer = None - if os.access(mountpoint + "/etc/redhat-release", os.R_OK): - f = open(mountpoint + "/etc/redhat-release", "r") - try: - lines = f.readlines() - except IOError: + filename = "%s/etc/redhat-release" % mountpoint + if os.access(filename, os.R_OK): + with open(filename) as f: try: - f.close() - except: - pass - return (relName, relVer) - - f.close() - - # return the first line with the newline at the end stripped - if len(lines) == 0: - return (relName, relVer) - - relstr = string.strip(lines[0][:-1]) + relstr = f.readline().strip() + except (IOError, AttributeError): + relstr = "" # 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") - relName = relstr[:idx - 1] - relVer = "" - - for a in relstr[idx + 8:]: - if a in string.digits + ".": - relVer += a - else: - break - - relstr = prod + " " + ver - except: - pass # don't worry, just use the relstr as we have it + (product, sep, version) = relstr.partition(" release ") + if sep: + relName = product + relVer = version.split()[0] return (relName, relVer) @@ -1238,8 +1237,13 @@ class FSSet(object): @property def migratableDevices(self): - """ Return a list of devices whose filesystems can be migrated. """ - return [] # no migrate for you!!! + """ List of devices whose filesystems can be migrated. """ + migratable = [] + for device in self.devices: + if device.format.migratable and device.format.exists: + migratable.append(device) + + return migratable def write(self, chroot="/"): """ write out all config files based on the set of filesystems """ -- cgit From 8ac4e853c782d440141c14b58b7fa752b552a3e5 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 12:00:39 -0600 Subject: Various changes to partitioning backend, autopart. - Add missing imports. - Add some debug logging to autopart entry function. - Actually advance the partition iterator to avoid infinite loop. - Add conversion of autopart reqs to Device instances. - A few trivial updates/fixes in doAutoPartition, doPartitioning, allocatePartitions, and growLVM. --- storage/partitioning.py | 134 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 95 insertions(+), 39 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index 6acea41ca..5a4e0b50f 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -20,6 +20,7 @@ # Red Hat Author(s): Dave Lehman # +import sys import os import copy from operator import add, sub @@ -27,8 +28,11 @@ from operator import add, sub import parted from pykickstart.constants import * +from constants import * + from errors import * from deviceaction import * +from devices import PartitionDevice import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -37,6 +41,13 @@ import logging log = logging.getLogger("storage") def doAutoPartition(anaconda): + log.debug("doAutoPartition(%s)" % anaconda) + log.debug("doAutoPart: %s" % anaconda.id.storage.doAutoPart) + log.debug("clearPartType: %s" % anaconda.id.storage.clearPartType) + log.debug("clearPartDisks: %s" % anaconda.id.storage.clearPartDisks) + log.debug("autoPartitionRequests: %s" % anaconda.id.storage.autoPartitionRequests) + log.debug("storage.disks: %s" % anaconda.id.storage.disks) + log.debug("all names: %s" % [d.name for d in anaconda.id.storage.devicetree.devices.values()]) if anaconda.dir == DISPATCH_BACK: anaconda.id.storage.reset() return @@ -52,12 +63,15 @@ def doAutoPartition(anaconda): part = disk.partedDisk.getFirstPartition() while part: if not part.type & parted.PARTITION_FREESPACE: + part = part.nextPartition() continue if part.getSize(unit="MB") > 100: disks.append(disk) break + part = part.nextPartition() + # create a separate pv partition for each disk with free space pvs = [] for disk in disks: @@ -68,29 +82,32 @@ def doAutoPartition(anaconda): anaconda.id.storage.createDevice(part) pvs.append(part) - # create a vg containing all of the new pvs - vg = anaconda.id.storage.newVG(pvs=pvs) - anaconda.id.storage.createDevice(vg) - - # convert storage.autoPartitionRequests into Device instances and + # + # Convert storage.autoPartitionRequests into Device instances and # schedule them for creation - for request in storage.autoPartitionRequests: + # + # First pass is for partitions only. We'll do LVs later. + # + for request in anaconda.id.storage.autoPartitionRequests: (mountpoint, fstype, size, maxsize, grow, asvol) = request - if asvol - dev = anaconda.id.storage.newLV(vg=vg, - fmt_type=fstype, - mountpoint=mountpoint, - size=size) - else: - dev = anaconda.id.storage.newPartition(fmt_type=fstype, - size=size, - disks=disks) + if asvol: + continue + + if fstype is None: + fstype = anaconda.id.storage.defaultFSType + + dev = anaconda.id.storage.newPartition(fmt_type=fstype, + size=size, + disks=disks) if grow: dev.req_grow = True if maxsize: dev.req_max_size = maxsize + if mountpoint: + dev.format.mountpoint = mountpoint + # schedule the device for creation anaconda.id.storage.createDevice(dev) @@ -99,7 +116,7 @@ def doAutoPartition(anaconda): # sanity check the individual devices log.warning("not sanity checking devices because I don't know how yet") - # run the autopart function to allocate partitions, grow, &c + # run the autopart function to allocate and grow partitions try: doPartitioning(anaconda.id.storage) except PartitioningWarning as msg: @@ -126,15 +143,52 @@ def doAutoPartition(anaconda): if anaconda.isKickstart: sys.exit(0) + # create a vg containing all of the autopart pvs + vg = anaconda.id.storage.newVG(pvs=pvs) + anaconda.id.storage.createDevice(vg) + + # + # Convert storage.autoPartitionRequests into Device instances and + # schedule them for creation. + # + # Second pass, for LVs only. + for request in anaconda.id.storage.autoPartitionRequests: + (mountpoint, fstype, size, maxsize, grow, asvol) = request + if not asvol: + continue + + if fstype is None: + fstype = anaconda.id.storage.defaultFSType + + # FIXME: move this to a function and handle exceptions + dev = anaconda.id.storage.newLV(vg=vg, + fmt_type=fstype, + mountpoint=mountpoint, + size=size) + + if grow: + dev.req_grow = True + if maxsize: + dev.req_max_size = maxsize + + if mountpoint: + dev.format.mountpoint = mountpoint + + # schedule the device for creation + anaconda.id.storage.createDevice(dev) + + # grow the new VG and its LVs + growLVM(anaconda.id.storage) + # sanity check the collection of devices log.warning("not sanity checking storage config because I don't know how yet") # now do a full check of the requests - (errors, warnings) = storage.sanityCheck() + (errors, warnings) = anaconda.id.storage.sanityCheck() if warnings: for warning in warnings: log.warning(warning) if errors: - errortxt = string.join(errors, '\n') + errortxt = "\n".join(errors) if anaconda.isKickstart: extra = _("\n\nPress 'OK' to exit the installer.") else: @@ -174,8 +228,7 @@ def clearPartitions(storage): - Needs some error handling, especially for the parted bits. """ - if not storage.clearPartType or \ - storage.clearPartType == CLEARPART_TYPE_NONE: + if storage.clearPartType == CLEARPART_TYPE_NONE: # not much to do return @@ -197,7 +250,7 @@ def clearPartitions(storage): parted.PARTITION_LOGICAL): continue - if clearPartType == CLEARPART_TYPE_ALL: + if storage.clearPartType == CLEARPART_TYPE_ALL: clear = True else: if part.format and part.format.linuxNative: @@ -417,7 +470,7 @@ def getBestFreeSpaceRegion(disk, part_type, req_size, return best_free -def doPartitioning(storage, exclusiveDisks=[]): +def doPartitioning(storage): """ Allocate and grow partitions. When this function returns without error, all PartitionDevice @@ -430,16 +483,14 @@ def doPartitioning(storage, exclusiveDisks=[]): storage -- the Storage instance - Keyword arguments: - - exclusiveDisks -- a list of basenames of disks to use - """ - disks = storage.disks + disks = [d for d in storage.disks if d.name in storage.clearPartDisks] partitions = storage.partitions - if exclusiveDisks: - # only use specified disks - disks = [d for d in disks if d.name in exclusiveDisks] + + # FIXME: isn't there a better place for this to happen? + bootDev = storage.fsset.bootDevice + if not bootDev.exists: + bootDev.req_bootable = True # FIXME: make sure non-existent partitions have empty parents list allocatePartitions(disks, partitions) @@ -449,13 +500,19 @@ def doPartitioning(storage, exclusiveDisks=[]): # them to the tree now for disk in disks: extended = disk.partedDisk.getExtendedPartition() - if extended.getDeviceNodeName() in [p.name for p in partitions]: + if not extended or \ + extended.getDeviceNodeName() in [p.name for p in partitions]: # this extended partition is preexisting continue + # This is a little odd because normally instantiating a partition + # that does not exist means leaving self.parents empty and instead + # populating self.req_disks. In this case, we need to skip past + # that since this partition is already defined. device = PartitionDevice(extended.getDeviceNodeName(), parents=disk) - device.setPartedPartition(extended) + device.parents = [disk] + device.partedPartition = extended storage.createDevice(device) def allocatePartitions(disks, partitions): @@ -614,7 +671,7 @@ def allocatePartitions(disks, partitions): constraint=disk.device.getConstraint()) # constraint=parted.Constraint(device=disk.device)) log.debug("created partition %s of %dMB and added it to %s" % (partition.getDeviceNodeName(), partition.getSize(), disk)) - _part.setPartedPartition(partition) + _part.partedPartition = partition _part.disk = _disk @@ -706,10 +763,10 @@ def growPartitions(disks, partitions): max_grow = (max_sect - sectors) # don't grow beyond the resident filesystem's max size - if part.format and getattr(part.format, 'maxsize', 0): - log.debug("format maxsize: %dMB" % part.format.maxsize) + if part.format.maxSize > 0: + log.debug("format maxsize: %dMB" % part.format.maxSize) # FIXME: round down to nearest cylinder boundary - max_sect = (part.format.maxsize * (1024 * 1024)) / sectorSize + max_sect = (part.format.maxSize * (1024 * 1024)) / sectorSize if max_sect < sectors + max_grow: max_grow = (max_sect - sectors) @@ -782,10 +839,9 @@ def growPartitions(disks, partitions): continue part.req_size = part.req_base_size -def growLVM(tree): +def growLVM(storage): """ Grow LVs according to the sizes of the PVs. """ - vgs = tree.getDevicesByType("lvm vg") - for vg in vgs: + for vg in storage.vgs: total_free = vg.freeSpace if not total_free: log.debug("vg %s has no free space" % vg.name) -- cgit From a728c1eb81bc2a2c87353df342e68e649055d6f5 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 12:21:10 -0600 Subject: Various changes to Device classes, esp. PartitionDevice. - Fix some imports. - Move setup methods to after setting self.exists to True in create methods. Doh! - Generally prepend an underscore to names of methods that are intended only for use as property getter/setter. - Don't call getFormat when initially defining StorageDevice._format since later in __init__ we will set it via the format property, which will call getFormat. - Create currentSize property to get actual size of a device, regardless of pending changes. - Modify size property to reflect pending changes to size. - Pass device's existence to base format ctor in _setFormat. - Add a commit method to DiskDevice to commit pending changes. - Use name instead of device in PartitionDevice ctor like we do in all the others. - Lots of little fixes to access of parted.Partition data. - Add methods to wrap parted.Partition flag ops. - Add some flag setting to PartitionDevice.create - Use lvs property in LVMVolumeGroupDevice's status method/property since it gets called from the ctor before we've set up lvNames. --- storage/devices.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 1670f5a71..b39afe439 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -520,8 +520,6 @@ class StorageDevice(Device): self.format.destroy() self.exists = False - for parent in self.parents: - parent.removeChild() class DiskDevice(StorageDevice): @@ -624,8 +622,6 @@ class DiskDevice(StorageDevice): self.partedDisk.commit() self.teardown() - for parent in self.parents: - parent.removeChild() def setup(self, intf=None): """ Open, or set up, a device. """ @@ -906,8 +902,6 @@ class PartitionDevice(StorageDevice): parted.deletePartition(self.partedPartition) self.exists = False - for parent in self.parents: - parent.removeChild() def _getSize(self): """ Get the device's size. """ @@ -1335,7 +1329,6 @@ class LVMVolumeGroupDevice(DMDevice): self.createParents() self.setupParents() lvm.vgcreate(self.name, pv_list, self.peSize) - self.notifyKernel() self.exists = True self.setup() @@ -1350,8 +1343,6 @@ class LVMVolumeGroupDevice(DMDevice): lvm.vgremove(self.name) self.notifyKernel() self.exists = False - for parent in self.parents: - parent.removeChild() def reduce(self, pv_list): """ Remove the listed PVs from the VG. """ @@ -1610,8 +1601,6 @@ class LVMLogicalVolumeDevice(DMDevice): self.teardown() lvm.lvremove(self.vg.name, self._name) self.exists = False - for parent in self.parents: - parent.removeChild() def resize(self, intf=None): # XXX should we set up anything before doing this? @@ -1908,8 +1897,6 @@ class MDRaidArrayDevice(StorageDevice): disk.format.destroy() self.exists = False - for parent in self.parents: - parent.removeChild() class DMRaidArrayDevice(DMDevice): @@ -2127,8 +2114,6 @@ class FileDevice(StorageDevice): os.unlink(self.path) self.exists = False - for parent in self.parents: - parent.removeChild() class DirectoryDevice(FileDevice): @@ -2161,8 +2146,6 @@ class DirectoryDevice(FileDevice): os.unlink(self.path) self.exists = False - for parent in self.parents: - parent.removeChild() class iScsiDiskDevice(DiskDevice): -- cgit From fad2b7d86566d5608e4d58642c816eeae960cf71 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 12:39:34 -0600 Subject: Use correct attributes of storage for clearpart type and disks. --- installclass.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/installclass.py b/installclass.py index 91f6c23b4..bc25c66ca 100644 --- a/installclass.py +++ b/installclass.py @@ -198,13 +198,10 @@ class BaseInstallClass(object): autorequests.append((None, "swap", minswap, maxswap, True, True)) if doClear: - storage.autoClearPartType = clear - storage.autoClearPartDrives = [] + storage.clearPartType = clear + storage.clearPartDisks = [] - if useLVM: - storage.autoPartitionRequests = autoCreateLVMPartitionRequests(autorequests) - else: - storage.autoPartitionRequests = autoCreatePartitionRequests(autorequests) + storage.autoPartitionRequests = autorequests def setInstallData(self, anaconda): -- cgit From 482c9c767d4eda51e1eedaaa85810b2e435b42d0 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 12:50:18 -0600 Subject: Fix iutil.execWithFoo's file handling. --- iutil.py | 60 +++++++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 21 deletions(-) diff --git a/iutil.py b/iutil.py index 7bedf25bf..87b1c45d2 100644 --- a/iutil.py +++ b/iutil.py @@ -41,8 +41,8 @@ log = logging.getLogger("anaconda") # @param searchPath Should command be searched for in $PATH? # @param root The directory to chroot to before running command. # @return The return code of command. -def execWithRedirect(command, argv, stdin = 0, stdout = 1, stderr = 2, - searchPath = 0, root = '/'): +def execWithRedirect(command, argv, stdin = None, stdout = None, + stderr = None, searchPath = 0, root = '/'): def chroot (): os.chroot(root) @@ -50,21 +50,27 @@ def execWithRedirect(command, argv, stdin = 0, stdout = 1, stderr = 2, raise RuntimeError, command + " can not be run" argv = list(argv) - if type(stdin) == type("string"): + if isinstance(stdin, str): if os.access(stdin, os.R_OK): stdin = open(stdin) else: - stdin = 0 - if type(stdout) == type("string"): + stdin = sys.stdin + elif stdin is None or not isinstance(stdin, file): + stdin = sys.stdin + + if isinstance(stdout, str): stdout = open(stdout, "w") - if type(stderr) == type("string"): + elif stdout is None or not isinstance(stdout, file): + stdout = sys.stdout + + if isinstance(stderr, str): stderr = open(stderr, "w") + elif stderr is None or not isinstance(stderr, file): + stderr = sys.stderr runningLog = open("/tmp/program.log", "a") runningLog.write("Running... %s\n" % ([command] + argv,)) - - if stdout is not None and type(stdout) != int: - stdout.write("Running... %s\n" %([command] + argv,)) + stdout.write("Running... %s\n" %([command] + argv,)) try: proc = subprocess.Popen([command] + argv, stdin=stdin, @@ -101,21 +107,25 @@ def execWithRedirect(command, argv, stdin = 0, stdout = 1, stderr = 2, # @param stderr The file descriptor to redirect stderr to. # @param root The directory to chroot to before running command. # @return The output of command from stdout. -def execWithCapture(command, argv, stdin = 0, stderr = 2, root='/'): +def execWithCapture(command, argv, stdin = None, stderr = None, root='/'): def chroot(): os.chroot(root) rc = "" argv = list(argv) - if type(stdin) == type("string"): + if isinstance(stdin, str): if os.access(stdin, os.R_OK): stdin = open(stdin) else: - stdin = 0 + stdin = sys.stdin + elif stdin is None or not isinstance(stdin, file): + stdin = sys.stdin - if type(stderr) == type("string"): + if isinstance(stderr, str): stderr = open(stderr, "w") + elif stderr is None or not isinstance(stderr, file): + stderr = sys.stderr runningLog = open("/tmp/program.log", "a") runningLog.write("Running... %s\n" % ([command] + argv,)) @@ -143,23 +153,31 @@ def execWithCapture(command, argv, stdin = 0, stderr = 2, root='/'): return rc -def execWithPulseProgress(command, argv, stdin = 0, stdout = 1, stderr = 2, - progress = None, root = '/'): +def execWithPulseProgress(command, argv, stdin = None, stdout = None, + stderr = None, progress = None, root = '/'): def chroot(): os.chroot(root) argv = list(argv) - if type(stdin) == type("string"): + if isinstance(stdin, str): if os.access(stdin, os.R_OK): stdin = open(stdin) else: - stdin = 0 - if type(stdout) == type("string"): + stdin = sys.stdin + elif stdin is None or not isinstance(stdin, file): + stdin = sys.stdin + + if isinstance(stdout, str): stdout = open(stdout, "w") - if type(stderr) == type("string"): + elif stdout is None or not isinstance(stdout, file): + stdout = sys.stdout + + if isinstance(stderr, str): stderr = open(stderr, "w") - if stdout is not None and type(stdout) != int: - stdout.write("Running... %s\n" %([command] + argv,)) + elif stderr is None or not isinstance(stderr, file): + stderr = sys.stderr + + stdout.write("Running... %s\n" %([command] + argv,)) p = os.pipe() childpid = os.fork() -- cgit From cbc5a7a1e6e5a29ac2b26486c407af2bf66cb30b Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 13:41:19 -0600 Subject: Remove doMigrateFilesystems, log storage to tty3. Also let exceptions get raised in turnOnFilesystems for the time being. --- packages.py | 199 +++++++++++++++++++++++++------------------------ storage/storage_log.py | 10 ++- upgrade.py | 1 + 3 files changed, 109 insertions(+), 101 deletions(-) diff --git a/packages.py b/packages.py index 5e51c8135..9d763896f 100644 --- a/packages.py +++ b/packages.py @@ -87,119 +87,122 @@ def copyAnacondaLogs(anaconda): except: pass -def doMigrateFilesystems(anaconda): - if anaconda.dir == DISPATCH_BACK: - return DISPATCH_NOOP - - if anaconda.id.fsset.haveMigratedFilesystems(): - return DISPATCH_NOOP - - anaconda.id.fsset.migrateFilesystems (anaconda) - - if anaconda.id.upgrade: - # if we're upgrading, we may need to do lvm device node hackery - anaconda.id.fsset.makeLVMNodes(anaconda.rootPath, trylvm1 = 1) - # and we should write out a new fstab with the migrated fstype - shutil.copyfile("%s/etc/fstab" % anaconda.rootPath, "%s/etc/fstab.anaconda" % anaconda.rootPath) - anaconda.id.fsset.write(anaconda.rootPath) - # and make sure /dev is mounted so we can read the bootloader - bindMountDevDirectory(anaconda.rootPath) - def turnOnFilesystems(anaconda): if anaconda.dir == DISPATCH_BACK: - log.info("unmounting filesystems") - anaconda.id.fsset.umountFilesystems(anaconda.rootPath) - return + if not anaconda.id.upgrade: + log.info("unmounting filesystems") + anaconda.id.storage.fsset.umountFilesystems(anaconda.rootPath) + return DISPATCH_NOOP if flags.setupFilesystems: if not anaconda.id.upgrade: - if not anaconda.id.fsset.isActive(): + if not anaconda.id.storage.fsset.active: # turn off any swaps that we didn't turn on # needed for live installs iutil.execWithRedirect("swapoff", ["-a"], stdout = "/dev/tty5", stderr="/dev/tty5", searchPath = 1) - try: - anaconda.id.storage.processActions(anaconda.intf) - except DeviceResizeError as (msg, device): - # XXX does this make any sense? do we support resize of - # devices other than partitions? - anaconda.intf.detailedMessageWindow(_("Device Resize Failed"), - _("An error was encountered while " - "resizing device %s.") % (device,), - msg, - type = "custom", - custom_buttons = [_("_Exit installer")]) - sys.exit(1) - except DeviceCreateError as (msg, device): - anaconda.intf.detailedMessageWindow(_("Device Creation Failed"), - _("An error was encountered while " - "creating device %s.") % (device,), - msg, - type = "custom", - custom_buttons = [_("_Exit installer")]) - sys.exit(1) - except DeviceDestroyError as (msg, device): - anaconda.intf.detailedMessageWindow(_("Device Removal Failed"), - _("An error was encountered while " - "removing device %s.") % (device,), - msg, - type = "custom", - custom_buttons = [_("_Exit installer")]) - sys.exit(1) - except DeviceError as (msg, device): - anaconda.intf.detailedMessageWindow(_("Device Setup Failed"), - _("An error was encountered while " - "setting up device %s.") % (device,), - msg, - type = "custom", - custom_buttons = [_("_Exit installer")]) - sys.exit(1) - except FSResizeError as (msg, device): - if os.path.exists("/tmp/resize.out"): - details = open("/tmp/resize.out", "r").read() - else: - details = "%s" %(msg,) - anaconda.intf.detailedMessageWindow(_("Resizing Failed"), - _("There was an error encountered while " - "resizing the device %s.") %(device,), - details, - type = "custom", - custom_buttons = [_("_Exit installer")]) - sys.exit(1) - except FSMigrateError as (msg, device): - anaconda.intf.detailedMessageWindow(_("Migration Failed"), - _("An error was encountered while " - "migrating filesystem on device %s.") - % (device,), - msg, - type = "custom", - custom_buttons = [_("_Exit installer")]) - sys.exit(1) - except FormatCreateError as (msg, device): - anaconda.intf.detailedMessageWindow(_("Formatting Failed"), - _("An error was encountered while " - "formatting device %s.") % (device,), - msg, - type = "custom", - custom_buttons = [_("_Exit installer")]) - sys.exit(1) - except Exception as msg: - # catch-all - anaconda.intf.detailedMessageWindow(_("Storage Activation Failed"), - _("An error was encountered while " - "activating your storage configuration.") - msg, - type = "custom", - custom_buttons = [_("_Exit installer")]) - sys.exit(1) - - anaconda.id.fsset.turnOnSwap(anaconda.intf) + upgrade_migrate = False + if anaconda.id.upgrade: + for d in anaconda.id.storage.fsset.migratableDevices: + if d.migrate: + upgrade_migrate = True + + try: + anaconda.id.storage.doIt() + except Exception: + # better to get ful exceptions for debugging + raise + except DeviceResizeError as (msg, device): + # XXX does this make any sense? do we support resize of + # devices other than partitions? + anaconda.intf.detailedMessageWindow(_("Device Resize Failed"), + _("An error was encountered while " + "resizing device %s.") % (device,), + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except DeviceCreateError as (msg, device): + anaconda.intf.detailedMessageWindow(_("Device Creation Failed"), + _("An error was encountered while " + "creating device %s.") % (device,), + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except DeviceDestroyError as (msg, device): + anaconda.intf.detailedMessageWindow(_("Device Removal Failed"), + _("An error was encountered while " + "removing device %s.") % (device,), + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except DeviceError as (msg, device): + anaconda.intf.detailedMessageWindow(_("Device Setup Failed"), + _("An error was encountered while " + "setting up device %s.") % (device,), + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except FSResizeError as (msg, device): + if os.path.exists("/tmp/resize.out"): + details = open("/tmp/resize.out", "r").read() + else: + details = "%s" %(msg,) + anaconda.intf.detailedMessageWindow(_("Resizing Failed"), + _("There was an error encountered while " + "resizing the device %s.") %(device,), + details, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except FSMigrateError as (msg, device): + anaconda.intf.detailedMessageWindow(_("Migration Failed"), + _("An error was encountered while " + "migrating filesystem on device %s.") + % (device,), + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except FormatCreateError as (msg, device): + anaconda.intf.detailedMessageWindow(_("Formatting Failed"), + _("An error was encountered while " + "formatting device %s.") % (device,), + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + except Exception as msg: + # catch-all + anaconda.intf.detailedMessageWindow(_("Storage Activation Failed"), + _("An error was encountered while " + "activating your storage configuration."), + msg, + type = "custom", + custom_buttons = [_("_Exit installer")]) + sys.exit(1) + + if not anaconda.id.upgrade: + anaconda.id.storage.fsset.turnOnSwap(anaconda.intf) anaconda.id.storage.fsset.mountFilesystems(anaconda, raiseErrors=False, readOnly=False, skipRoot=anaconda.backend.skipFormatRoot) + else: + if upgrade_migrate: + # we should write out a new fstab with the migrated fstype + shutil.copyfile("%s/etc/fstab" % anaconda.rootPath, + "%s/etc/fstab.anaconda" % anaconda.rootPath) + anaconda.id.storage.fsset.write(anaconda.rootPath) + + # and make sure /dev is mounted so we can read the bootloader + bindMountDevDirectory(anaconda.rootPath) + def setupTimezone(anaconda): # we don't need this on an upgrade or going backwards diff --git a/storage/storage_log.py b/storage/storage_log.py index 351df9811..a771154f0 100644 --- a/storage/storage_log.py +++ b/storage/storage_log.py @@ -2,12 +2,16 @@ import logging #handler = logging.StreamHandler() -handler = logging.FileHandler("/tmp/storage.log") +file_handler = logging.FileHandler("/tmp/storage.log") formatter = logging.Formatter("[%(asctime)s] %(levelname)8s: %(message)s") -handler.setFormatter(formatter) +file_handler.setFormatter(formatter) + +tty3_handler = logging.FileHandler("/dev/tty3") +tty3_handler.setFormatter(formatter) logger = logging.getLogger("storage") -logger.addHandler(handler) +logger.addHandler(file_handler) +logger.addHandler(tty3_handler) logger.setLevel(logging.DEBUG) diff --git a/upgrade.py b/upgrade.py index c7e8efd5f..e89742a40 100644 --- a/upgrade.py +++ b/upgrade.py @@ -317,6 +317,7 @@ def setSteps(anaconda): "upgrademigfind", "upgrademigratefs", "upgradearchitecture", + "enablefilesystems", "upgradecontinue", "reposetup", "upgbootloader", -- cgit From 9950180ab90fdce68f9993939fc09c72066f2143 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 14:29:27 -0600 Subject: Add an attribute to indicate whether the FSSet is active. --- storage/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/storage/__init__.py b/storage/__init__.py index f30326520..7ea1f5f10 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -855,6 +855,7 @@ class FSSet(object): self.devicetree = devicetree self.cryptTab = None self.blkidTab = None + self.active = False @property def devices(self): @@ -1170,6 +1171,8 @@ class FSSet(object): log.error("FSError: %s" % msg) sys.exit(0) + self.active = True + def umountFilesystems(self, instPath, ignoreErrors=True, swapoff=True): # XXX if we tracked the /dev bind mount this wouln't be necessary if os.path.ismount("%s/dev" % instPath): @@ -1186,6 +1189,8 @@ class FSSet(object): device.teardownFormat() device.teardown() + self.active = False + def createSwapFile(self, rootPath, device, size): """ Create and activate a swap file under rootPath. """ filename = "/SWAP" -- cgit From b63f8cf9af990529b597e515752e1ee18430fabf Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 26 Feb 2009 15:41:27 -0500 Subject: hasiSeriesNativeStorage is no longer used anywhere. --- iutil.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/iutil.py b/iutil.py index 87b1c45d2..749ddee8f 100644 --- a/iutil.py +++ b/iutil.py @@ -350,25 +350,6 @@ def copyDeviceNode(src, dest): os.mknod(dest, mode | type, filestat.st_rdev) -## Determine if the hardware supports iSeries storage devices. -# @return 1 if so, 0 otherwise. -def hasiSeriesNativeStorage(): - # this is disgusting and I feel very dirty - if not isPPC(): - return - - f = open("/proc/modules", "r") - lines = f.readlines() - f.close() - - for line in lines: - if line.startswith("ibmsis"): - return 1 - if line.startswith("ipr"): - return 1 - - return 0 - ## Get the PPC machine variety type. # @return The PPC machine type, or 0 if not PPC. def getPPCMachine(): -- cgit From 8ec692a16d4cbe1940b524716c87759144e092bb Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 26 Feb 2009 15:41:28 -0500 Subject: Remove bootDevice and bootloaderChoices from the storage module. --- storage/__init__.py | 64 ----------------------------------------------------- 1 file changed, 64 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 7ea1f5f10..a2a95c679 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1344,70 +1344,6 @@ class FSSet(object): options, dump, passno) return fstab - @property - def bootDevice(self): - mntDict = {} - bootDev = None - for device in [d for d in self.devices if d.format.mountable]: - mntDict[device.format.mountpoint] = device - - # FIXME: this ppc stuff feels kind of crufty -- the abstraction - # here needs a little bit of work - if iutil.getPPCMacGen() == "NewWorld": - for device in self.devicetree.devices.values(): - # XXX do we need to also check the size? - if device.format.type == "hfs" and device.bootable: - bootDev = device - elif (iutil.getPPCMachine() == "pSeries" or - iutil.getPPCMachine() == "iSeries"): - # we want the first prep partition or the first newly formatted one - # XXX who cares if PReP is formatted? it has no format. - partitions = self.devicetree.getDeviceByType("partition") - for device in partitions: - if device.partedPartition.getFlag(parted.PARTITION_PREP) and \ - (bestprep is None or \ - (bestprep.format.exists and not device.format.exists)): - bootDev = device - elif iutil.isEfi(): - bootDev = mntDict.get('/boot/efi') - else: - bootDev = mntDict.get("/boot", mntDict.get("/")) - - # XXX should we raise an error if no boot device was found? - return bootDev - - def bootloaderChoices(self, bl): - ret = {} - bootDev = self.bootDevice - - if bootDev is None: - log.warning("no boot device set") - return ret - - if iutil.isEfi(): - ret['boot'] = (bootDev.path, N_("EFI System Partition")) - elif bootDev.device.getName() == "RAIDDevice": - ret['boot'] = (bootDev.path, N_("RAID Device")) - ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) - elif iutil.getPPCMacGen() == "NewWorld": - ret['boot'] = (bootDev.path, N_("Apple Bootstrap")) - partitions = self.devicetree.getDevicesByType("partition") - for (n, device) in enumerate(partitions): - if device.format.type == "hfs" and device.bootable and \ - device.path != bootDev.path: - ret['boot%d' %(n,)] = (device.path, - N_("Apple Bootstrap")) - elif (iutil.getPPCMachine() == "pSeries" or - iutil.getPPCMachine() == "iSeries"): - ret['boot'] = (bootDev.device, N_("PPC PReP Boot")) - else: - ret['boot'] = (bootDev.device, - N_("First sector of boot partition")) - ret['mbr'] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) - - return ret - - def resolveDevice(tree, devspec, blkidTab=None, cryptTab=None): # find device in the tree device = None -- cgit From c433e6d175e068db7e606f0bf5f8ccfafcae5bc9 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 26 Feb 2009 15:41:29 -0500 Subject: Remove autopart.py. delete mode 100644 autopart.py --- autopart.py | 151 ----------------------------------------------------------- kickstart.py | 1 - 2 files changed, 152 deletions(-) delete mode 100644 autopart.py diff --git a/autopart.py b/autopart.py deleted file mode 100644 index a74dac7cf..000000000 --- a/autopart.py +++ /dev/null @@ -1,151 +0,0 @@ -# -# autopart.py - auto partitioning logic -# -# Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007 Red Hat, Inc. -# All rights reserved. -# -# This program is free software; you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation; either version 2 of the License, or -# (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program. If not, see . -# -# Author(s): Jeremy Katz -# - -import parted -import copy -import string, sys -import logging -from anaconda_log import logger, logFile -import partedUtils -from constants import * -from errors import * - -import iutil -import isys - -log = logging.getLogger("anaconda") - -import gettext -_ = lambda x: gettext.ldgettext("anaconda", x) - - -PARTITION_FAIL = -1 -PARTITION_SUCCESS = 0 - -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" partitions meet necessary constraints unless -# the request has its ignore flag set -def bootRequestCheck(req, diskset): - if not req.device or req.ignoreBootConstraints: - return PARTITION_SUCCESS - - part = None - - if not hasattr(req, "drive"): - return PARTITION_SUCCESS - - for drive in req.drive: - part = diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device) - - if not part: - return PARTITION_SUCCESS - - if iutil.isEfi(): - if req.mountpoint == "/boot": - if not part.fileSystem.type.startswith("ext"): - return BOOT_NOT_EXT2 - elif req.mountpoint == "/boot/efi": - if not part.fileSystem.type in ["fat16", "fat32"]: - return BOOTEFI_NOT_VFAT - elif iutil.isAlpha(): - return bootAlphaCheckRequirements(part) - elif (iutil.getPPCMachine() == "pSeries" or - iutil.getPPCMachine() == "iSeries"): - for drive in req.drive: - part = diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device) - if part and ((part.geometry.end * part.geometry.device.sectorSize / - (1024.0 * 1024)) > 4096): - return BOOTIPSERIES_TOO_HIGH - - 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 == "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.getFirstPartition() - while free: - if free.type & parted.PARTITION_FREESPACE: - break - free = free.nextPartition() - if (not free or free.geometry.start != 1L or free.getSize(unit="MB") < 1): - return BOOTALPHA_NO_RESERVED_SPACE - - return PARTITION_SUCCESS - -def getMinimumSector(disk): - if disk.type == 'sun': - (cylinders, heads, sectors) = disk.device.biosGeometry - start = long(sectors * heads) - start /= long(1024 / disk.device.sectorSize) - return start + 1 - return 0L - -def getAutopartitionBoot(partitions): - """Return the proper shorthand for the boot dir (arch dependent).""" - fsname = fsset.fileSystemTypeGetDefaultBoot().getName() - if iutil.isEfi(): - ret = [ ["/boot/efi", "efi", 50, 200, 1, 1, 0] ] - for req in partitions.requests: - if req.fstype == fsset.fileSystemTypeGet("efi") \ - and not req.mountpoint: - req.mountpoint = "/boot/efi" - ret = [] - ret.append(("/boot", fsname, 200, None, 0, 1, 0)) - return ret - elif (iutil.getPPCMachine() == "pSeries"): - return [ (None, "PPC PReP Boot", 4, None, 0, 1, 0), - ("/boot", fsname, 200, None, 0, 1, 0) ] - elif (iutil.getPPCMachine() == "iSeries") and not iutil.hasiSeriesNativeStorage(): - return [ (None, "PPC PReP Boot", 16, None, 0, 1, 0) ] - elif (iutil.getPPCMachine() == "iSeries") and iutil.hasiSeriesNativeStorage(): - return [] - elif (iutil.getPPCMachine() == "PMac") and iutil.getPPCMacGen() == "NewWorld": - return [ ( None, "Apple Bootstrap", 1, 1, 0, 1, 0), - ("/boot", fsname, 200, None, 0, 1, 0) ] - else: - return [ ("/boot", fsname, 200, None, 0, 1, 0) ] - - diff --git a/kickstart.py b/kickstart.py index 0fee19e07..c09d62a22 100644 --- a/kickstart.py +++ b/kickstart.py @@ -23,7 +23,6 @@ import iutil import isys import os import tempfile -from autopart import * from flags import flags from constants import * import sys -- cgit From 972a07b9b4d365333b47683d558db8c29cf4b0c4 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 26 Feb 2009 15:41:32 -0500 Subject: Fix calls to getAutopartitionBoot, calling the platform's code instead. --- installclass.py | 6 +++--- installclasses/fedora.py | 1 + installclasses/rhel.py | 1 + kickstart.py | 2 +- 4 files changed, 6 insertions(+), 4 deletions(-) diff --git a/installclass.py b/installclass.py index bc25c66ca..b74b5bad6 100644 --- a/installclass.py +++ b/installclass.py @@ -186,11 +186,11 @@ class BaseInstallClass(object): from backend import AnacondaBackend return AnacondaBackend - def setDefaultPartitioning(self, storage, clear = CLEARPART_TYPE_LINUX, - doClear = True, useLVM = True): + def setDefaultPartitioning(self, storage, platform, + clear = CLEARPART_TYPE_LINUX, doClear = True): autorequests = [ ("/", None, 1024, None, True, True) ] - bootreq = getAutopartitionBoot(storage) + bootreq = platform.setDefaultPartitioning() if bootreq: autorequests.extend(bootreq) diff --git a/installclasses/fedora.py b/installclasses/fedora.py index 1fdfb05c4..c183a6eea 100644 --- a/installclasses/fedora.py +++ b/installclasses/fedora.py @@ -65,6 +65,7 @@ class InstallClass(BaseInstallClass): if not anaconda.isKickstart: BaseInstallClass.setDefaultPartitioning(self, anaconda.id.storage, + anaconda.platform, CLEARPART_TYPE_LINUX) def setSteps(self, anaconda): diff --git a/installclasses/rhel.py b/installclasses/rhel.py index d179372b1..95bacdf47 100644 --- a/installclasses/rhel.py +++ b/installclasses/rhel.py @@ -90,6 +90,7 @@ class InstallClass(BaseInstallClass): if not anaconda.isKickstart: BaseInstallClass.setDefaultPartitioning(self, anaconda.id.storage, + anaconda.platform, CLEARPART_TYPE_LINUX) def setSteps(self, anaconda): diff --git a/kickstart.py b/kickstart.py index c09d62a22..bc8028ace 100644 --- a/kickstart.py +++ b/kickstart.py @@ -143,7 +143,7 @@ class AutoPart(commands.autopart.F9_AutoPart): # sets up default autopartitioning. use clearpart separately # if you want it - self.handler.id.instClass.setDefaultPartitioning(self.handler.id, doClear = 0) + self.handler.id.instClass.setDefaultPartitioning(self.handler.id.storage, self.handler.anaconda.platform, doClear = 0) if self.encrypted: self.handler.id.storage.autoEncrypt = True -- cgit From d265e74d9bd22b8e460e031f09f11ec4c1866194 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 26 Feb 2009 15:41:26 -0500 Subject: Added a set of classes that encapsulate platform-specific information. This is where we'll be storing bootloader, disk type, default partitioning, and other similar information that changes on a per-platform basis. All this information needs to be removed from everywhere else. --- platform.py | 395 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 395 insertions(+) create mode 100644 platform.py diff --git a/platform.py b/platform.py new file mode 100644 index 000000000..818f6af73 --- /dev/null +++ b/platform.py @@ -0,0 +1,395 @@ +# +# platform.py: Architecture-specific information +# +# Copyright (C) 2009 +# Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +# Authors: Chris Lumens +# + +import iutil +import parted +import storage +from storage.errors import * + +import gettext +_ = lambda x: gettext.ldgettext("anaconda", x) +N_ = lambda x: x + +class Platform(object): + """Platform + + A class containing platform-specific information and methods for use + during installation. The intent is to eventually encapsulate all the + architecture quirks in one place to avoid lots of platform checks + throughout anaconda.""" + _bootFSType = "ext3" + _diskType = parted.diskType["msdos"] + _minimumSector = 0 + + def __init__(self, anaconda): + """Creates a new Platform object. This is basically an abstract class. + You should instead use one of the platform-specific classes as + returned by getPlatform below. Not all subclasses need to provide + all the methods in this class.""" + self.anaconda = anaconda + + def _mntDict(self): + """Return a dictionary mapping mount points to devices.""" + ret = {} + for device in [d for d in self.anaconda.id.storage.devices if d.format.mountable]: + ret[device.format.mountpoint] = device + + return ret + + def bootDevice(self): + """Return the device where /boot is mounted.""" + if self.__class__ is Platform: + raise NotImplementedError("bootDevice not implemented for this platform") + + mntDict = self._mntDict() + bootDev = mntDict.get("/boot", mntDict.get("/")) + + if not bootDev: + raise DeviceError("No bootable device found") + else: + return bootDev + + @property + def bootFSType(self): + """Return the default filesystem type for the boot partition.""" + return self._bootFSType + + def bootloaderChoices(self, bl): + """Return the default list of places to install the bootloader. + This is returned as a dictionary of locations to (device, identifier) + tuples. If there is no boot device, an empty dictionary is + returned.""" + if self.__class__ is Platform: + raise NotImplementedError("bootloaderChoices not implemented for this platform") + + bootDev = self.bootDevice() + ret = {} + + if not bootDev: + return ret + + if bootDev.device.getName() == "RAIDDevice": + ret["boot"] = (bootDev.path, N_("RAID Device")) + ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) + else: + ret["boot"] = (bootDev.device, N_("First sector of boot partition")) + ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) + + return ret + + def checkBootRequest(self, req, diskset): + """Perform an architecture-specific check on the boot device. Not all + platforms may need to do any checks. Raises an exception if there + is a problem, or returns True otherwise.""" + return + + @property + def diskType(self): + """Return the disk label type as a parted.DiskType.""" + return self._diskType + + @diskType.setter + def diskType(self, value): + """Sets the disk label type.""" + self._diskType = value + + @property + def minimumSector(self, disk): + """Return the minimum starting sector for the provided disk.""" + return self._minimumSector + + def setDefaultPartitioning(self): + """Return the default platform-specific partitioning information.""" + return [("/boot", self.bootFSType, 200, None, 0, 0)] + +class EFI(Platform): + _diskType = parted.diskType["gpt"] + + def bootDevice(self): + mntDict = self._mntDict() + bootDev = mntDict.get("/boot/efi") + + if not bootDev: + raise DeviceError("No bootable device found") + else: + return bootDev + + def bootloaderChoices(self, bl): + bootDev = self.bootDevice() + ret = {} + + if not bootDev: + return ret + + ret["boot"] = (bootDev.path, N_("EFI System Partition")) + return ret + + def checkBootRequest(self, req, diskset): + if not req.device or not hasattr(req, "drive"): + return + + bootPart = None + for drive in req.drive: + bootPart = diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device) + if bootPart: + break + + if not bootPart: + return + + if req.mountpoint == "/boot": + if not bootPart.fileSystem.type.startswith("ext"): + raise FSError("/boot is not ext2") + elif req.mountpoint == "/boot/efi": + if not bootPart.fileSystem.type.startswith("fat"): + raise FSError("/boot/efi is not vfat") + + def setDefaultPartitioning(self): + ret = Platform.setDefaultPartitioning(self) + + # Only add the EFI partition to the default set if there's not already + # one on the system. + if len(filter(lambda dev: dev.format.type == "efi" and dev.size < 256 and dev.bootable, + self.anaconda.id.storage.partitions)) == 0: + ret.append(("/boot/efi", "efi", 50, 200, 1, 0)) + + return ret + +class Alpha(Platform): + _diskType = parted.diskType["bsd"] + + def checkBootRequest(self, req, diskset): + if not req.device or not hasattr(req, "drive"): + return + + bootPart = None + for drive in req.drive: + bootPart = diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device) + if bootPart: + break + + if not bootPart: + return + + disk = bootPart.disk + + # Check that we're a BSD disk label + if not disk.type == self.diskType: + raise DeviceError("Disk label is not %s" % self.diskType) + + # The first free space should start at the beginning of the drive and + # span for a megabyte or more. + free = disk.getFirstPartition() + while free: + if free.type & parted.PARTITION_FREESPACE: + break + + free = free.nextPartition() + + if not free or free.geoemtry.start != 1L or free.getSize(unit="MB") < 1: + raise DeviceError("Disk does not have enough free space at the beginning") + + return + +class IA64(EFI): + def __init__(self, anaconda): + EFI.__init__(self, anaconda) + +class PPC(Platform): + _ppcMachine = iutil.getPPCMachine() + + @property + def ppcMachine(self): + return self._ppcMachine + +class IPSeriesPPC(PPC): + def bootDevice(self): + bootDev = None + + # We want the first PReP partition. + for device in storage.partitions: + if device.partedPartition.getFlag(parted.PARTITION_PREP): + bootDev = device + break + + if not bootDev: + raise DeviceError("No bootable device found") + else: + return bootDev + + def bootloaderChoices(self, bl): + ret = {} + + bootDev = self.bootDevice() + if not bootDev: + return ret + + if bootDev.device.getName() == "RAIDDevice": + ret["boot"] = (bootDev.path, N_("RAID Device")) + ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) + else: + ret["boot"] = (bootDev.device, N_("PPC PReP Boot")) + + return ret + + def checkBootRequest(self, req, diskset): + if not req.device or not hasattr(req, "drive"): + return + + bootPart = None + for drive in req.drive: + bootPart = diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device) + if bootPart and (bootPart.geometry.end * bootPart.geometry.device.sectorSize / + (1024.0 * 1024)) > 4096: + raise DeviceError("Boot partition is located too high") + + return + + def setDefaultPartitioning(self): + ret = PPC.setDefaultPartitioning(self) + ret.insert(0, (None, "PPC PReP Boot", 4, None, 0, 0)) + return ret + +class NewWorldPPC(PPC): + _diskType = parted.diskType["mac"] + + def bootDevice(self): + bootDev = None + + for device in self.anaconda.id.storage.devices.values(): + # XXX do we need to also check the size? + if device.format.type == "hfs" and device.bootable: + bootDev = device + + if not bootDev: + raise DeviceError("No bootable device found") + else: + return bootDev + + def bootloaderChoices(self, bl): + ret = {} + + bootDev = self.bootDevice() + if not bootDev: + return ret + + if bootDev.device.getName() == "RAIDDevice": + ret["boot"] = (bootDev.path, N_("RAID Device")) + ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) + else: + ret["boot"] = (bootDev.path, N_("Apple Bootstrap")) + for (n, device) in enumerate(self.anaconda.id.storage.partitions): + if device.format.type == "hfs" and device.bootable and device.path != bootDev.path: + ret["boot%d" % n] = (device.path, N_("Apple Bootstrap")) + + return ret + + def setDefaultPartitioning(self): + ret = Platform.setDefaultPartitioning(self) + ret.insert(0, (None, "Apple Bootstrap", 1, 1, 0, 0)) + return ret + +class S390(Platform): + def __init__(self, anaconda): + Platform.__init__(self, anaconda) + +class Sparc(Platform): + _diskType = parted.diskType["sun"] + + @property + def minimumSector(self, disk): + (cylinders, heads, sector) = disk.device.biosGeometry + start = long(sectors * heads) + start /= long(1024 / disk.device.sectorSize) + return start+1 + +class X86(EFI): + _isEfi = iutil.isEfi() + + def __init__(self, anaconda): + EFI.__init__(self, anaconda) + + if self.isEfi: + self.diskType = parted.diskType["gpt"] + else: + self.diskType = parted.diskType["msdos"] + + def bootDevice(self): + if self.isEfi: + return EFI.bootDevice(self) + else: + return Platform.bootDevice(self) + + def bootloaderChoices(self, bl): + if self.isEfi: + return EFI.bootloaderChoices(self, bl) + + bootDev = self.bootDevice() + ret = {} + + if not bootDev: + return {} + + if bootDev.device.getName() == "RAIDDevice": + ret["boot"] = (bootDev.path, N_("RAID Device")) + ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) + else: + ret["boot"] = (bootDev.device, N_("First sector of boot partition")) + ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) + + return ret + + @property + def isEfi(self): + return self._isEfi + + def setDefaultPartitioning(self): + if self.isEfi: + return EFI.setDefaultPartitioning(self) + else: + return Platform.setDefaultPartitioning(self) + +def getPlatform(anaconda): + """Check the architecture of the system and return an instance of a + Platform subclass to match. If the architecture could not be determined, + raise an exception.""" + if iutil.isAlpha(): + return Alpha(anaconda) + elif iutil.isIA64(): + return IA64(anaconda) + elif iutil.isPPC(): + ppcMachine = iutil.getPPCMachine() + + if ppcMachine == "PMac" and iutil.getPPCMacGen() == "NewWorld": + return NewWorldPPC(anaconda) + elif ppcMachine in ["iSeries", "pSeries"]: + return IPSeriesPPC(anaconda) + else: + raise SystemError, "Unsupported PPC machine type" + elif iutil.isS390(): + return S390(anaconda) + elif iutil.isSparc(): + return Sparc(anaconda) + elif iutil.isX86(): + return X86(anaconda) + else: + raise SystemError, "Could not determine system architecture." -- cgit From e94357c09d66c01cab18938cb7aa221a0e8e68a7 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 26 Feb 2009 15:41:30 -0500 Subject: Add a Platform instance to the anaconda object. --- anaconda | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/anaconda b/anaconda index 5958a2a27..6ded5dbb8 100755 --- a/anaconda +++ b/anaconda @@ -502,6 +502,7 @@ class Anaconda: self.rescue = False self.updateSrc = None self.mediaDevice = None + self.platform = None # *sigh* we still need to be able to write this out self.xdriver = None @@ -634,6 +635,9 @@ if __name__ == "__main__": import gettext _ = lambda x: gettext.ldgettext("anaconda", x) + import platform + anaconda.platform = platform.getPlatform(anaconda) + if not iutil.isS390() and os.access("/dev/tty3", os.W_OK): logger.addFileHandler ("/dev/tty3", log) -- cgit From 4d193ccca95115fe5b0baa4c2f4955c473f3173c Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 26 Feb 2009 15:41:31 -0500 Subject: Fix up calls to bootloaderChoices to refer to the platform module now. --- bootloader.py | 2 +- iw/bootloader_main_gui.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bootloader.py b/bootloader.py index 99dada4eb..a5e4a946d 100644 --- a/bootloader.py +++ b/bootloader.py @@ -81,7 +81,7 @@ def bootloaderSetupChoices(anaconda): if bootPart: anaconda.id.bootloader.setDevice(bootPart) - choices = anaconda.id.storage.fsset.bootloaderChoices(anaconda.id.bootloader) + choices = anaconda.platform.bootloaderChoices(anaconda.id.bootloader) if not choices and iutil.getPPCMachine() != "iSeries": anaconda.dispatch.skipStep("instbootloader") else: diff --git a/iw/bootloader_main_gui.py b/iw/bootloader_main_gui.py index c674d3261..b8991b40e 100644 --- a/iw/bootloader_main_gui.py +++ b/iw/bootloader_main_gui.py @@ -110,7 +110,7 @@ class MainBootloaderWindow(InstallWindow): dialog.set_transient_for(self.parent) dialog.show() - choices = anaconda.id.storage.fsset.bootloaderChoices(self.bl) + choices = anaconda.platform.bootloaderChoices(self.bl) for t in ("mbr", "boot"): if not choices.has_key(t): continue @@ -202,7 +202,7 @@ class MainBootloaderWindow(InstallWindow): else: # we don't know what it is yet... if mbr is possible, we want # it, else we want the boot dev - choices = anaconda.id.storage.fsset.bootloaderChoices(self.bl) + choices = anaconda.platform.bootloaderChoices(self.bl) if choices.has_key('mbr'): self.bldev = choices['mbr'][0] else: -- cgit From 621f76f3f249730f65d92c67abb2fb8fd932baef Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 26 Feb 2009 15:41:33 -0500 Subject: Move bootloader package information into the platform module. --- platform.py | 11 +++++++++++ yuminstall.py | 12 +----------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/platform.py b/platform.py index 818f6af73..ef9ff0c8d 100644 --- a/platform.py +++ b/platform.py @@ -37,6 +37,7 @@ class Platform(object): architecture quirks in one place to avoid lots of platform checks throughout anaconda.""" _bootFSType = "ext3" + _bootloaderPackage = None _diskType = parted.diskType["msdos"] _minimumSector = 0 @@ -96,6 +97,10 @@ class Platform(object): return ret + @property + def bootloaderPackage(self): + return self._bootloaderPackage + def checkBootRequest(self, req, diskset): """Perform an architecture-specific check on the boot device. Not all platforms may need to do any checks. Raises an exception if there @@ -211,10 +216,13 @@ class Alpha(Platform): return class IA64(EFI): + _bootloaderPackage = "elilo" + def __init__(self, anaconda): EFI.__init__(self, anaconda) class PPC(Platform): + _bootloaderPackage = "yaboot" _ppcMachine = iutil.getPPCMachine() @property @@ -309,6 +317,8 @@ class NewWorldPPC(PPC): return ret class S390(Platform): + _bootloaderPackage = "s390utils" + def __init__(self, anaconda): Platform.__init__(self, anaconda) @@ -323,6 +333,7 @@ class Sparc(Platform): return start+1 class X86(EFI): + _bootloaderPackage = "grub" _isEfi = iutil.isEfi() def __init__(self, anaconda): diff --git a/yuminstall.py b/yuminstall.py index fd859d84e..d66aef67b 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -1282,17 +1282,6 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon log.debug("selecting kernel-devel") self.selectPackage("kernel-devel.%s" % (kpkg.arch,)) - def selectBootloader(self): - if iutil.isX86(): - self.selectPackage("grub") - elif iutil.isS390(): - self.selectPackage("s390utils") - elif iutil.isPPC(): - self.selectPackage("yaboot") - # XXX this needs to become grub, and we need an upgrade path... - elif iutil.isIA64(): - self.selectPackage("elilo") - def selectFSPackages(self, storage): for device in storage.fsset.devices: # this takes care of device and filesystem packages @@ -1326,6 +1315,7 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon if not anaconda.id.getUpgrade(): # New installs only - upgrades will already have all this stuff. self.selectBestKernel(anaconda) + self.selectPackage(anaconda.platform.bootloaderPackage) self.selectBootloader() self.selectFSPackages(anaconda.id.storage) self.selectAnacondaNeeds() -- cgit From e1309eee078b508458c36b9d4c2a6d5b275e6796 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 26 Feb 2009 15:41:34 -0500 Subject: Get rid of partedUtils.getDefaultDiskType, since the platform knows that. --- partedUtils.py | 32 +++----------------------------- 1 file changed, 3 insertions(+), 29 deletions(-) diff --git a/partedUtils.py b/partedUtils.py index 6d33a98bc..dc64ee3e0 100644 --- a/partedUtils.py +++ b/partedUtils.py @@ -114,32 +114,6 @@ def filter_partitions(disk, func): rc.append(part) return rc -def getDefaultDiskType(): - """Get the default partition table type for this architecture.""" - if iutil.isEfi(): - return parted.diskType["gpt"] - elif iutil.isX86(): - return parted.diskType["msdos"] - elif iutil.isS390(): - # the "default" type is dasd, but we don't really do dasd - # formatting with parted and use dasdfmt directly for them - # so if we get here, it's an fcp disk and we should write - # an msdos partition table (#144199) - return parted.diskType["msdos"] - elif iutil.isAlpha(): - return parted.diskType["bsd"] - elif iutil.isSparc(): - return parted.diskType["sun"] - elif iutil.isPPC(): - ppcMachine = iutil.getPPCMachine() - - if ppcMachine == "PMac": - return parted.diskType["mac"] - else: - return parted.diskType["msdos"] - else: - return parted.diskType["msdos"] - def hasGptLabel(diskset, device): disk = diskset.disks[device] return disk.type == "gpt" @@ -153,9 +127,9 @@ def isEfiSystemPartition(part): part.fileSystem.type in ("fat16", "fat32") and isys.readFSLabel(part.getDeviceNodeName()) != "ANACONDA") -def labelDisk(deviceFile, forceLabelType=None): +def labelDisk(platform, deviceFile, forceLabelType=None): dev = parted.getDevice(deviceFile) - label = getDefaultDiskType() + label = platform.diskType if not forceLabelType is None: label = forceLabelType @@ -883,7 +857,7 @@ class DiskSet: dev = parted.getDevice(deviceFile) disk = parted.Disk(device=dev) else: - disk = labelDisk(deviceFile) + disk = labelDisk(self.anaconda.platform, deviceFile) except Exception, msg: log.error("parted error: %s" % (msg,)) raise -- cgit From 1010676cf6b5f36dbd54c0888a182c0c930644fe Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 15:39:59 -0600 Subject: First crack at making upgrade migrate gui work with new storage code. --- iw/upgrade_migratefs_gui.py | 54 ++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/iw/upgrade_migratefs_gui.py b/iw/upgrade_migratefs_gui.py index bd683b68e..d71e3d8bc 100644 --- a/iw/upgrade_migratefs_gui.py +++ b/iw/upgrade_migratefs_gui.py @@ -24,7 +24,6 @@ from constants import * import string import isys import iutil -from fsset import * import gtk import gettext @@ -34,28 +33,30 @@ class UpgradeMigrateFSWindow (InstallWindow): windowTitle = N_("Migrate File Systems") def getNext (self): - for entry in self.migent: - entry.setFormat(0) - entry.setMigrate(0) - entry.fsystem = entry.origfsystem - + # I don't like this but I also don't see a better way right now for (cb, entry) in self.cbs: + action = self.devicetree.findActions(device=entry, + type="migrate") if cb.get_active(): - try: - newfs = entry.fsystem.migratetofs[0] - newfs = fileSystemTypeGet(newfs) - except Exception, e: - log.info("failed to get new filesystem type, defaulting to ext3: %s" %(e,)) - newfs = fileSystemTypeGet("ext3") - entry.setFileSystemType(newfs) - entry.setFormat(0) - entry.setMigrate(1) - + if action: + # the migrate action has already been scheduled + continue + + newfs = getFormat(entry.format.migrationTarget) + if not newfs: + log.warning("failed to get new filesystem type (%s)" + % entry.format.migrationTarget) + continue + action = ActionMigrateFormat(entry) + self.devicetree.registerAction(action) + elif action: + self.devicetree.cancelAction(action) + return None def getScreen (self, anaconda): - - self.fsset = anaconda.id.fsset + self.devicetree = anaconda.id.storage.devicetree + self.fsset = anaconda.id.storage.fsset self.migent = self.fsset.getMigratableEntries() box = gtk.VBox (False, 5) @@ -79,17 +80,16 @@ class UpgradeMigrateFSWindow (InstallWindow): self.cbs = [] for entry in self.migent: # don't allow the user to migrate /boot to ext4 (#439944) - if entry.mountpoint == "/boot" and entry.origfsystem.getName() == "ext3": + if (getattr(entry.format, "mountpoint", None) == "/boot" and + not entry.format.migrate and entry.format.type == "ext3"): continue - if entry.fsystem.getName() != entry.origfsystem.getName(): - migrating = 1 - else: - migrating = 0 - cb = gtk.CheckButton("/dev/%s - %s - %s" % (entry.device.getDevice(), - entry.origfsystem.getName(), - entry.mountpoint)) - cb.set_active(migrating) + cb = gtk.CheckButton("%s - %s - %s" % (entry.path, + entry.format.name, + getattr(entry.format, + "mountpoint", + None))) + cb.set_active(entry.format.migrate) cbox.pack_start(cb, False) self.cbs.append((cb, entry)) -- cgit From d3c97b7a5f5b7ff6e011726ef44a582d0d0234bb Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 16:25:03 -0600 Subject: Specify the filesystem type in the autopart requests. --- installclass.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installclass.py b/installclass.py index b74b5bad6..47ea1c62b 100644 --- a/installclass.py +++ b/installclass.py @@ -188,7 +188,7 @@ class BaseInstallClass(object): def setDefaultPartitioning(self, storage, platform, clear = CLEARPART_TYPE_LINUX, doClear = True): - autorequests = [ ("/", None, 1024, None, True, True) ] + autorequests = [ ("/", storage.defaultFSType, 1024, None, True, True) ] bootreq = platform.setDefaultPartitioning() if bootreq: -- cgit From 137d93a32d7c927b988d2d4180870bee58e24ef1 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 17:06:40 -0600 Subject: Various changes to Device classes, esp. PartitionDevice. (take 2) - Fix some imports. - Move setup methods to after setting self.exists to True in create methods. Doh! - Generally prepend an underscore to names of methods that are intended only for use as property getter/setter. - Don't call getFormat when initially defining StorageDevice._format since later in __init__ we will set it via the format property, which will call getFormat. - Create currentSize property to get actual size of a device, regardless of pending changes. - Modify size property to reflect pending changes to size. - Pass device's existence to base format ctor in _setFormat. - Add a commit method to DiskDevice to commit pending changes. - Use name instead of device in PartitionDevice ctor like we do in all the others. - Lots of little fixes to access of parted.Partition data. - Add methods to wrap parted.Partition flag ops. - Add some flag setting to PartitionDevice.create - Use lvs property in LVMVolumeGroupDevice's status method/property since it gets called from the ctor before we've set up lvNames. --- storage/devices.py | 235 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 156 insertions(+), 79 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index b39afe439..3cc25994b 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -96,10 +96,10 @@ import os import math # device backend modules -import devicelibs.mdraid -import devicelibs.lvm +from devicelibs import mdraid +from devicelibs import lvm #import block -import devicelibs.dm +from devicelibs import dm import parted from errors import * @@ -359,7 +359,7 @@ class StorageDevice(Device): Device.__init__(self, device, parents=parents) self.uuid = None - self._format = getFormat(None) + self._format = None self._size = numeric_type(size) self.major = numeric_type(major) self.minor = numeric_type(minor) @@ -371,6 +371,7 @@ class StorageDevice(Device): self.format = format self.fstabComment = "" + self.targetSize = self._size @property def path(self): @@ -410,7 +411,6 @@ class StorageDevice(Device): log.debug("not sending change uevent for inactive device") return - self.updateSysfsPath() path = os.path.normpath("/sys/%s" % self.sysfsPath) try: notify_kernel(path, action="change") @@ -453,16 +453,28 @@ class StorageDevice(Device): self.teardownParents(recursive=recursive) def _getSize(self): - """ Get the device's size. """ - return self._size + """ Get the device's size, accounting for pending changes. """ + size = self._size + if self.resizable and self.targetSize != size: + size = self.targetSize + return size def _setSize(self, newsize): """ Set the device's size to a new value. """ # FIXME: this should involve some verification self._size = newsize - size = property(lambda x: x._getSize(), lambda x, y: x._setSize(y), - doc="The device's size") + size = property(lambda x: x._getSize(), + #lambda x, y: x._setSize(y), + doc="The device's size, accounting for pending changes") + + @property + def currentSize(self): + """ The device's actual size. """ + size = 0 + if self.exists: + size = self._size + return size @property def status(self): @@ -479,10 +491,10 @@ class StorageDevice(Device): def _setFormat(self, format): """ Set the Device's format. """ if not format: - format = getFormat(None, device=self.path) + format = getFormat(None, device=self.path, exists=self.exists) log_method_call(self, self.name, type=format.type, - current=self._format.type, status=self.status) - if self.format and self._format.status: + current=getattr(self._format, "type", None)) + if self._format and self._format.status: # FIXME: self.format.status doesn't mean much raise DeviceError("cannot replace active format") @@ -503,8 +515,8 @@ class StorageDevice(Device): self.createParents() self.setupParents() - self.setup() self.exists = True + self.setup() def destroy(self): """ Destroy the device. """ @@ -520,6 +532,9 @@ class StorageDevice(Device): self.format.destroy() self.exists = False + # we already did this in DeviceTree._removeDevice + #for parent in self.parents: + # parent.removeChild() class DiskDevice(StorageDevice): @@ -596,19 +611,18 @@ class DiskDevice(StorageDevice): log_method_call(self, self.name, size=self.size, partedDevice=self.partedDevice) if not self.size: - self.size = self.partedDevice.getSize() + self._size = self.partedDevice.getSize() if not self.diskLabel: log.debug("setting %s diskLabel to %s" % (self.name, self.partedDisk.type)) self.diskLabel = self.partedDisk.type - def create(self, intf=None): - """ Create the device. """ + def commit(self, intf=None): + """ Commit changes to the device. """ log_method_call(self, self.name, status=self.status) - self.createParents() self.setupParents() - self.partedDisk.commit() self.setup() + self.partedDisk.commit() def destroy(self): """ Destroy the device. """ @@ -641,17 +655,35 @@ class PartitionDevice(StorageDevice): On types and flags... - We don't need to deal with numerical partition types at all. - - The only type we are concerned with is primary/logical/extended. - - Usage specification is accomplished through the use of flags, - which we will set according to the partition's format. + We don't need to deal with numerical partition types at all. The + only type we are concerned with is primary/logical/extended. Usage + specification is accomplished through the use of flags, which we + will set according to the partition's format. + + + On usage of parted... + + We are doing this in a slightly lame way right now. We do all + partitioning on the disks' partedDisk instances and update the + partitions' partedPartition instances accordingly. What this means + is that when the time comes to commit changes, all a + PartitionDevice must do to make the changes take effect is make + sure self.disk.partedDisk.commit() is called. Probably better + would be to work with copies of the parted.Disks during + partitioning, save the modified parted.Partitions in the + PartitionDevice instances, and call + self.disk.partedDisk.deletePartition in destroy and + self.disk.partedDisk.addPartition, followed by + self.disk.partedDisk.commit() in create. + + OTOH, the upside to the current approach is that both the disks' + and the partitions' parted instances are in agreement in + reflecting the future layout. """ _type = "partition" _resizable = True - def __init__(self, device, format=None, + def __init__(self, name, format=None, size=None, grow=False, maxsize=None, major=None, minor=None, bootable=None, sysfsPath='', parents=None, exists=None, @@ -660,7 +692,7 @@ class PartitionDevice(StorageDevice): Arguments: - device -- the device name (generally a device node's basename) + name -- the device name (generally a device node's basename) Keyword Arguments: @@ -692,7 +724,9 @@ class PartitionDevice(StorageDevice): self.req_base_size = 0 self.req_max_size = 0 - StorageDevice.__init__(self, device, format=format, size=size, + self.bootable = False + + StorageDevice.__init__(self, name, format=format, size=size, major=major, minor=minor, exists=exists, sysfsPath=sysfsPath, parents=parents) @@ -742,12 +776,15 @@ class PartitionDevice(StorageDevice): @property def partType(self): """ Get the partition's type (as parted constant). """ - if self.partedPartition: - return self.partedPartition.type - else: - if self.exists: - raise DeviceError("partition exists but has no partedPartition") - return self._partType + try: + ptype = self.partedPartition.type + except AttributeError: + ptype = self._partType + + if not self.exists and ptype is None: + ptype = self.req_partType + + return ptype @property def isExtended(self): @@ -765,26 +802,19 @@ class PartitionDevice(StorageDevice): def isProtected(self): return self.partType & parted.PARTITION_PROTECTED - @property - def getPartedPartition(self): - return self._partedPartition - log.debug("PartitionDevice %s has %d parents (%s)" % (self.name, - len(self.parents), self.parents)) + def _getPartedPartition(self): ppart = None - if len(self.parents) == 1: - if self._partedPartition and \ - self.disk.partedDisk == self._partedPartition.disk: - return self._partedPartition - - log.debug("path is %s ; partitions is %s" % (self.path, - [p.path for p in self.disk.partedDisk.partitions])) + if self._partedPartition and self.disk and \ + self.disk.partedDisk == self._partedPartition.disk: + ppart = self._partedPartition + elif self.disk: pdisk = self.disk.partedDisk ppart = pdisk.getPartitionByPath(self.path) self._partedPartition = ppart return ppart - def setPartedPartition(self, partition): + def _setPartedPartition(self, partition): """ Set this PartitionDevice's parted Partition instance. """ log_method_call(self, self.name) if partition is None: @@ -800,8 +830,8 @@ class PartitionDevice(StorageDevice): self._partedPartition = partition self._name = partition.getDeviceNodeName() - partedPartition = property(lambda d: d.getPartedPartition(), - lambda d,p: d.setPartedPartition(p)) + partedPartition = property(lambda d: d._getPartedPartition(), + lambda d,p: d._setPartedPartition(p)) def dependsOn(self, dep): """ Return True if this device depends on dep. """ @@ -821,9 +851,6 @@ class PartitionDevice(StorageDevice): def setBootable(self, bootable): """ Set the bootable flag for this partition. """ - if 'parted' not in globals().keys(): - return - if self.partedPartition: if isFlagAvailable(parted.PARTITION_BOOT): if bootable: @@ -840,6 +867,38 @@ class PartitionDevice(StorageDevice): else: self.bootable = bootable + def flagAvailable(self, flag): + log_method_call(self, path=self.path, flag=flag, + part=self.partedPartition) + if not self.partedPartition: + return + + return self.partedPartition.isFlagAvailable(flag) + + def getFlag(self, flag): + log_method_call(self, path=self.path, flag=flag, + part=self.partedPartition) + if not self.partedPartition or not self.flagAvailable(flag): + return + + return self.partedPartition.getFlag(flag) + + def setFlag(self, flag): + log_method_call(self, path=self.path, flag=flag, + part=self.partedPartition) + if not self.partedPartition or not self.flagAvailable(flag): + return + + self.partedPartition.setFlag(flag) + + def unsetFlag(self, flag): + log_method_call(self, path=self.path, flag=flag, + part=self.partedPartition) + if not self.partedPartition or not self.flagAvailable(flag): + return + + self.partedPartition.unsetFlag(flag) + def probe(self): """ Probe for any missing information about this device. @@ -863,7 +922,7 @@ class PartitionDevice(StorageDevice): """ # this is in MB - self.size = self.partedPartition.getSize() + self._size = self.partedPartition.getSize() self._partType = self.partedPartition.type @@ -873,14 +932,26 @@ class PartitionDevice(StorageDevice): if self.exists: raise DeviceError("device already exists") - # first, do the parted commit - try: - self.disk.create() - except DeviceError, e: - log.debug("error creating %s: %s" % (self.parents[0].name, e)) + self.createParents() + self.setupParents() - self.setup() + # If we are bootable or our format has a flag it needs set, do + # that now. + # + # FIXME: this should be moved onto a common codepath so that + # flags get set properly for existing partitions as well. + # + # XXX this may trigger multiple parted.Disk.commit() calls, but + # who cares? + if self.format.partedFlag is not None: + self.setFlag(self.format.partedFlag) + + if self.bootable: + self.setFlag(parted.PARTITION_BOOT) + + self.disk.commit() self.exists = True + self.setup() def destroy(self): """ Destroy the device. """ @@ -894,24 +965,27 @@ class PartitionDevice(StorageDevice): if not self.isleaf: raise DeviceError("Cannot destroy non-leaf device") + self.setupParents() if self.status: self.format.destroy() - # now actually tell pyparted to remove the partition - # FIXME: need special handling for extended partitions - parted.deletePartition(self.partedPartition) + # We have already removed the partition from the in-memory + # parted.Disk, so there is nothing to do here except make sure + # self.disk.partedDisk.commit() gets called. + # + # We must have faith that there are no erroneous outstanding + # changes also queued for the disk. + self.disk.commit() self.exists = False def _getSize(self): """ Get the device's size. """ - if self.partedPartition: + size = self._size + if len(self.parents) == 1: # this defaults to MB - return self.partedPartition.getSize() - else: - if self.exists: - raise DeviceError("partition exists but has no partedPartition") - return self._size + size = self.partedPartition.getSize() + return size def _setSize(self, newsize): """ Set the device's size (for resize, not creation). @@ -1110,8 +1184,8 @@ class LUKSDevice(DMCryptDevice): #if not self.slave.format.exists: # self.slave.format.create() - self.setup() self.exists = True + self.setup() def setup(self, intf=None): """ Open, or set up, a device. """ @@ -1220,9 +1294,8 @@ class LVMVolumeGroupDevice(DMDevice): return False # certainly if any of this VG's LVs are active then so are we - for lvName in self.lvNames: - lvPath = os.path.normpath("%s-%s" % (self.path, lvName)) - if os.path.exists(lvPath): + for lv in self.lvs: + if lv.status: return True # if any of our PVs are not active then we cannot be @@ -1236,7 +1309,7 @@ class LVMVolumeGroupDevice(DMDevice): return True - def addDevice(self, device): + def _addDevice(self, device): """ Add a new physical volume device to the volume group. XXX This is for use by device probing routines and is not @@ -1265,7 +1338,7 @@ class LVMVolumeGroupDevice(DMDevice): if len(self.parents) == self.pvCount: self.setup() - def removeDevice(self, device): + def _removeDevice(self, device): """ Remove a physical volume from the volume group. This is for cases like clearing of preexisting partitions. @@ -1359,14 +1432,14 @@ class LVMVolumeGroupDevice(DMDevice): raise ValueError("lv is already part of this vg") # verify we have the space, then add it - if lv.size > self.freeSpace: + if not lv.exists and lv.size > self.freeSpace: raise DeviceError("new lv is too large to fit in free space") self._lvs.append(lv) def _removeLogVol(self, lv): """ Remove an LV from this VG. """ - if not lv in self.lvs: + if lv not in self.lvs: raise ValueError("specified lv is not part of this vg") self._lvs.remove(lv) @@ -1734,7 +1807,7 @@ class MDRaidArrayDevice(StorageDevice): else: self.sysfsPath = None - def addDevice(self, device): + def _addDevice(self, device): """ Add a new member device to the array. XXX This is for use when probing devices, not for modification @@ -1760,7 +1833,11 @@ class MDRaidArrayDevice(StorageDevice): self.devices.append(device) device.addChild() - def removeDevice(self, device): + if self.status: + device.setup() + mdraid.mdadd(device.path) + + def _removeDevice(self, device): """ Remove a component device from the array. XXX This is for use by clearpart, not for reconfiguration. @@ -2096,8 +2173,8 @@ class FileDevice(StorageDevice): try: buf = '\0' * 1024 * 1024 * self.size os.write(fd, buf) - except Exception, e: - log.error("error zeroing out %s: %s" % (self.device, e)) + except (OSError, TypeError) as e: + log.error("error writing out %s: %s" % (self.path, e)) finally: os.close(fd) -- cgit From a4a3afc402b0a1b4597a6f9f49e8074ddcdbe918 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 21:45:49 -0600 Subject: os.dup2 wants a fileno, not a file. Also, don't clobber the name str. --- iutil.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iutil.py b/iutil.py index 749ddee8f..2df995a7d 100644 --- a/iutil.py +++ b/iutil.py @@ -185,8 +185,8 @@ def execWithPulseProgress(command, argv, stdin = None, stdout = None, os.close(p[0]) os.dup2(p[1], 1) os.dup2(stderr.fileno(), 2) - os.dup2(stdin, 0) - os.close(stdin) + os.dup2(stdin.fileno(), 0) + stdin.close() os.close(p[1]) stderr.close() @@ -199,7 +199,7 @@ def execWithPulseProgress(command, argv, stdin = None, stdout = None, try: s = os.read(p[0], 1) except OSError, args: - (num, str) = args + (num, _str) = args if (num != 4): raise IOError, args -- cgit From 764fa60dc0b2a0b3c2b9b0b002343d1c7c5fd4ab Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 21:51:38 -0600 Subject: Remove little hack that throws off action sorting. --- storage/devicetree.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index ff95568e2..cdd41f070 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -249,12 +249,6 @@ class DeviceTree(object): elif y.isDevice() and not x.isDevice(): #log.debug("y is a device") return 1 - elif x.device.isleaf and not y.device.isleaf: - #log.debug("x is a leaf -- y first") - return 1 - elif y.device.isleaf and not x.device.isleaf: - #log.debug("y is a leaf -- x first") - return -1 #log.debug("no decision") return 0 -- cgit From 7cfc841f40b36ecc4dbb51be0226d18727af1ab2 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 21:52:53 -0600 Subject: Change partedFlags attribute to partedFlag. --- storage/formats/__init__.py | 2 +- storage/formats/lvmpv.py | 2 +- storage/formats/mdraid.py | 2 +- storage/formats/swap.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py index 13ed3d61f..1f64f7f4f 100644 --- a/storage/formats/__init__.py +++ b/storage/formats/__init__.py @@ -136,7 +136,7 @@ class DeviceFormat(object): _type = None _name = "Unknown" _udevTypes = [] - partedFlags = None + partedFlag = None _formattable = False # can be formatted _supported = False # is supported _linuxNative = False # for clearpart diff --git a/storage/formats/lvmpv.py b/storage/formats/lvmpv.py index e5dfa5837..a1c2e5322 100644 --- a/storage/formats/lvmpv.py +++ b/storage/formats/lvmpv.py @@ -40,7 +40,7 @@ class LVMPhysicalVolume(DeviceFormat): _type = "lvmpv" _name = "physical volume (LVM)" _udevTypes = ["LVM2_member"] - partedFlags = PARTITION_LVM + partedFlag = PARTITION_LVM _formattable = True # can be formatted _supported = True # is supported _linuxNative = True # for clearpart diff --git a/storage/formats/mdraid.py b/storage/formats/mdraid.py index fde4852d7..0871a2a5c 100644 --- a/storage/formats/mdraid.py +++ b/storage/formats/mdraid.py @@ -40,7 +40,7 @@ class MDRaidMember(DeviceFormat): _type = "mdmember" _name = "software RAID" _udevTypes = ["linux_raid_member"] - partedFlags = PARTITION_RAID + partedFlag = PARTITION_RAID _formattable = True # can be formatted _supported = True # is supported _linuxNative = True # for clearpart diff --git a/storage/formats/swap.py b/storage/formats/swap.py index 5f5958383..4740f3ae3 100644 --- a/storage/formats/swap.py +++ b/storage/formats/swap.py @@ -39,7 +39,7 @@ class SwapSpace(DeviceFormat): """ Swap space """ _type = "swap" _udevTypes = ["swap"] - partedFlags = PARTITION_SWAP + partedFlag = PARTITION_SWAP _formattable = True # can be formatted _supported = True # is supported _linuxNative = True # for clearpart -- cgit From 4280547fa4fef3c62e5b6f2c0f3b6793d02bfab3 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 27 Feb 2009 21:54:13 -0600 Subject: Use anaconda.platform to get boot device. Using anaconda.platform necessitates passing anaconda to doPartitioning instead of just passing anaconda.id.storage. --- storage/partitioning.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index 5a4e0b50f..17a10814a 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -118,7 +118,7 @@ def doAutoPartition(anaconda): # run the autopart function to allocate and grow partitions try: - doPartitioning(anaconda.id.storage) + doPartitioning(anaconda) except PartitioningWarning as msg: if not anaconda.isKickstart: anaconda.intf.messageWindow(_("Warnings During Automatic " @@ -470,7 +470,7 @@ def getBestFreeSpaceRegion(disk, part_type, req_size, return best_free -def doPartitioning(storage): +def doPartitioning(anaconda): """ Allocate and grow partitions. When this function returns without error, all PartitionDevice @@ -481,14 +481,15 @@ def doPartitioning(storage): Arguments: - storage -- the Storage instance + anaconda -- what else? """ + storage = anaconda.id.storage disks = [d for d in storage.disks if d.name in storage.clearPartDisks] partitions = storage.partitions # FIXME: isn't there a better place for this to happen? - bootDev = storage.fsset.bootDevice + bootDev = anaconda.platform.bootDevice() if not bootDev.exists: bootDev.req_bootable = True -- cgit From 12427eff13e5b9297ecb8354f6df752a3f240fb2 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 2 Mar 2009 12:24:26 -0600 Subject: We can expect to find the parted module now. --- storage/devices.py | 24 ++++++++++-------------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 3cc25994b..4703fa5c1 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -569,20 +569,16 @@ class DiskDevice(StorageDevice): self.partedDevice = None self.partedDisk = None self.removable = False - if 'parted' in globals().keys(): - log.debug("looking up parted Device: %s" % self.path) - self.partedDevice = parted.Device(path=self.path) - if not self.partedDevice: - raise DeviceError("cannot find parted device instance") - log.debug("creating parted Disk: %s" % self.path) - self.partedDisk = parted.Disk(device=self.partedDevice) - if not self.partedDisk: - raise DeviceError("failed to create parted Disk") - - self.probe() - - #def open(self): - # raise DeviceError, "shit's busted." + log.debug("looking up parted Device: %s" % self.path) + self.partedDevice = parted.Device(path=self.path) + if not self.partedDevice: + raise DeviceError("cannot find parted device instance") + log.debug("creating parted Disk: %s" % self.path) + self.partedDisk = parted.Disk(device=self.partedDevice) + if not self.partedDisk: + raise DeviceError("failed to create parted Disk") + + self.probe() @property def size(self): -- cgit From d9fb606310411979e98b3b888fb01d9dacfd90f8 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Fri, 27 Feb 2009 15:30:17 -1000 Subject: Use hashlib instead of md5 module in iscsi.py Same fix as on the master branch. Got tired of seeing the DeprecationWarning messages. --- storage/iscsi.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/storage/iscsi.py b/storage/iscsi.py index af06cfef5..fff6ad14a 100644 --- a/storage/iscsi.py +++ b/storage/iscsi.py @@ -29,7 +29,8 @@ from flags import flags import logging import shutil import time -import md5, random +import hashlib +import random import partedUtils log = logging.getLogger("anaconda") @@ -104,7 +105,7 @@ def randomIname(): """Generate a random initiator name the same way as iscsi-iname""" s = "iqn.1994-05.com.fedora:01." - m = md5.md5() + m = hashlib.md5() u = os.uname() for i in u: m.update(i) -- cgit From f673138c97e6d44a7ded497679a8e90300bc593a Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Sun, 1 Mar 2009 16:07:54 -1000 Subject: Use disk.name to get device node name. We have used string slicing to get a device node name like 'sda5' in the past. The disk.name property now gives us that so we do not have to do string slicing all over the code. --- iw/autopart_type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iw/autopart_type.py b/iw/autopart_type.py index 0c8e6c9c1..bb3853323 100644 --- a/iw/autopart_type.py +++ b/iw/autopart_type.py @@ -389,7 +389,7 @@ class PartitionTypeWindow(InstallWindow): size, partedDisk.device.model) i = bootstore.append(None) bootstore[i] = (dispstr, partedDisk.device.path[5:]) - if disk.device.path[5:] == defaultBoot: + if disk.name == defaultBoot: self.bootcombo.set_active_iter(i) if len(bootstore) <= 1: -- cgit From f7d1564cad1f542fdcdaa25487f49152a9a68041 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Sun, 1 Mar 2009 16:26:55 -1000 Subject: Syntax and import fixes. --- iw/lvm_dialog_gui.py | 4 ++-- iw/partition_dialog_gui.py | 6 +++--- iw/raid_dialog_gui.py | 8 ++++---- kickstart.py | 4 +--- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index ea6d7e831..6e093fef1 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -29,7 +29,7 @@ import datacombo import gui from partition_ui_helpers_gui import * from constants import * -import lvm +from storage.devices import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -671,7 +671,7 @@ class VolumeGroupEditor: if format: actions.append(ActionDestroyFormat(usedev)) - actions.append(ActionCreateFormat(usedev, format) + actions.append(ActionCreateFormat(usedev, format)) if luksdev: actions.append(ActionCreateDevice(luksdev)) diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index 272b8bd00..2fdf85604 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -202,7 +202,7 @@ class PartitionEditor: luksdev = LUKSDevice("luks%d" % self.storage.nextID, format=format, parents=request) - actions.append(ActionCreateDevice(luksdev) + actions.append(ActionCreateDevice(luksdev)) format = getFormat("luks", device=self.origrequest.path, passphrase=self.storage.encryptionPassphrase) @@ -217,7 +217,7 @@ class PartitionEditor: if self.fsoptionsDict.has_key("resizecb") and \ self.fsoptionsDict["resizecb"].get_active(): size = self.fsoptionsDict["resizesb"].get_value_as_int() - actions.append(ActionResizeDevice(request, size) + actions.append(ActionResizeDevice(request, size)) if request.format.type != "none": actions.append(ActionResizeFormat(request, size)) @@ -229,7 +229,7 @@ class PartitionEditor: if request.format.exists and \ getattr(request, "mountpoint", None) and \ - self.storage.formatByDefault(request)): + self.storage.formatByDefault(request): if not queryNoFormatPreExisting(self.intf): continue diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index 8b6ed5e55..8757bbc1e 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -28,7 +28,7 @@ import gtk import datacombo import gui -from raid import availRaidLevels +from storage.devices import * from partition_ui_helpers_gui import * from constants import * @@ -176,7 +176,7 @@ class RaidEditor: self.fsoptionsDict["lukscb"].get_active() and \ self.origrequest.format.type != "luks": luksdev = LUKSDevice("luks-%s" % request.name, - format=format + format=format, parents=self.origrequest) format = getFormat("luks", passphrase=self.storage.encryptionPassphrase) @@ -263,7 +263,7 @@ class RaidEditor: else: if origrequest.minor is not None: tstr = _("Edit RAID Device: %s") % (origrequest.path,) - except: + else: tstr = _("Edit RAID Device") dialog = gtk.Dialog(tstr, self.parent) @@ -377,7 +377,7 @@ class RaidEditor: if not origrequest.exists: - self.levelcombo = self.createRaidLevelMenu(availRaidLevels, + self.levelcombo = self.createRaidLevelMenu(raid_levels, origrequest.level) lbl.set_mnemonic_widget(self.levelcombo) else: diff --git a/kickstart.py b/kickstart.py index bc8028ace..f8e5467a5 100644 --- a/kickstart.py +++ b/kickstart.py @@ -26,14 +26,12 @@ import tempfile from flags import flags from constants import * import sys -import raid import string import urlgrabber.grabber as grabber -import lvm import warnings import upgrade import pykickstart.commands as commands -import cryptodev +from storage.devices import * import zonetab from pykickstart.constants import * from pykickstart.errors import * -- cgit From 0b10c389194d87acc9b8e9086e7fca495d8901c3 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Sun, 1 Mar 2009 17:44:24 -1000 Subject: Remove possibly unnecessary code in class NoDevFS I cannot find anywhere that 'nodev' is used. I played around with variants on these two lines, but eventually ended up removing them to continue with testing. --- storage/formats/fs.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 511ac3c01..f80a877db 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -933,8 +933,6 @@ class NoDevFS(FS): def __init__(self, *args, **kwargs): FS.__init__(self, *args, **kwargs) - if self.nodev and not self.device: - self.device = self.type def _deviceCheck(self, devspec): pass -- cgit From 5d67416b33d53c95cf0695502b00d5260f32f1fd Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Sun, 1 Mar 2009 19:56:52 -1000 Subject: doPartitioning() receives a Storage instance. Modify this function to accept a Storage instance rather than an Anaconda instance. --- storage/partitioning.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index 17a10814a..dd31b81c8 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -470,7 +470,7 @@ def getBestFreeSpaceRegion(disk, part_type, req_size, return best_free -def doPartitioning(anaconda): +def doPartitioning(storage): """ Allocate and grow partitions. When this function returns without error, all PartitionDevice @@ -481,10 +481,10 @@ def doPartitioning(anaconda): Arguments: - anaconda -- what else? + storage - Main anaconda Storage instance """ - storage = anaconda.id.storage + anaconda = storage.anaconda disks = [d for d in storage.disks if d.name in storage.clearPartDisks] partitions = storage.partitions -- cgit From c50d5cb9310041e5b402f17baaba743626b6b9c5 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Sun, 1 Mar 2009 20:00:17 -1000 Subject: self_migratable -> self._migratable Fix a typo. --- storage/formats/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py index 1f64f7f4f..860292362 100644 --- a/storage/formats/__init__.py +++ b/storage/formats/__init__.py @@ -316,7 +316,7 @@ class DeviceFormat(object): @property def migratable(self): """ Can formats of this type be migrated? """ - return self_migratable + return self._migratable @property def migrate(self): -- cgit From be2303f4986bc19fe542e85218bbcd6db9f25691 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Sun, 1 Mar 2009 20:03:57 -1000 Subject: Import doPartitioning from storage.partitioning. doPartitioning is not in autopart anymore. --- iw/partition_gui.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index 48e32ca55..87d756f19 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -44,6 +44,7 @@ import partition_dialog_gui from partIntfHelpers import * from constants import * from partition_ui_helpers_gui import * +from storage.partitioning import doPartitioning import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -989,7 +990,7 @@ class PartitionWindow(InstallWindow): self.tree.clear() try: - autopart.doPartitioning(self.storage) + doPartitioning(self.storage) rc = 0 except PartitioningError, msg: self.intf.messageWindow(_("Error Partitioning"), -- cgit From fbbe047b552af5fd306fa6226ed625f3e1afe9bd Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Sun, 1 Mar 2009 20:17:56 -1000 Subject: Syntax fixes for new storage code. Misc syntax fixes for the new storage code. Generated these while trying to do a 'Create custom layout' install. --- iw/partition_dialog_gui.py | 5 +++-- iw/partition_ui_helpers_gui.py | 11 +++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index 2fdf85604..d1af32da0 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -27,6 +27,7 @@ import gtk import gui from storage.devices import PartitionDevice +from storage.deviceaction import ActionCreateFormat from partition_ui_helpers_gui import * from constants import * @@ -190,14 +191,14 @@ class PartitionEditor: actions.append(ActionCreateDevice(request)) else: # preexisting partition, just set mount point and format flag - request = copy.copy(self.origrequest) + request = self.origrequest mountpoint = self.mountCombo.get_children()[0].get_text() if self.fsoptionsDict.has_key("formatcb") and \ self.fsoptionsDict["formatcb"].get_active(): fmt_class = self.fsoptionsDict["fstypeCombo"].get_active_value() format = fmt_class(mountpoint=mountpoint) if self.fsoptionsDict.has_key("lukscb") and \ - lukscb.get_active() and \ + self.fsoptionsDict["lukscb"].get_active() and \ request.format.type != "luks": luksdev = LUKSDevice("luks%d" % self.storage.nextID, format=format, diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index fa1871220..ad7303e85 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -30,7 +30,7 @@ import iutil from constants import * from partIntfHelpers import * from partedUtils import * -from storage.formats import device_formats, getFormat +from storage.formats import device_formats, getFormat, get_default_filesystem_type import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -185,11 +185,10 @@ def createFSTypeMenu(fstype, fstypechangeCB, mountCombo, store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) fstypecombo = datacombo.DataComboBox(store) - types = device_formats.keys() if availablefstypes: - names = availablefstypes + names = [ availablefstypes ] else: - names = types.keys() + names = device_formats.keys() fs = getFormat(fstype) if fs and fs.supported and fs.formattable: default = fstype @@ -209,7 +208,7 @@ def createFSTypeMenu(fstype, fstypechangeCB, mountCombo, if format.formattable: fstypecombo.append(name, device_formats[name]) - if default and default.type == name: + if default == name: defindex = i defismountable = format.mountable i = i + 1 @@ -324,7 +323,7 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, migratecb.set_active(istruefalse(origrequest.format.migrate)) # TODO: unimplemented - migtypes = origrequest.format.migrationTargets + migtypes = origrequest.format.migrationTarget maintable.attach(migratecb, 0, 1, row, row + 1) migfstypeCombo = createFSTypeMenu(ofstype, None, None, -- cgit From c286f14f18d24e7946481f19700fbdd75c153bd2 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Sun, 1 Mar 2009 20:06:39 -1000 Subject: Remove calls to sanityCheckRequest() We don't have this function anymore, but I don't know if we need to be calling anything else. --- iw/lvm_dialog_gui.py | 11 ----------- iw/partition_dialog_gui.py | 15 --------------- iw/raid_dialog_gui.py | 6 ------ 3 files changed, 32 deletions(-) diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index 6e093fef1..3aebb2032 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -675,17 +675,6 @@ class VolumeGroupEditor: if luksdev: actions.append(ActionCreateDevice(luksdev)) - err = self.storage.sanityCheckRequest(lv, - skipMntPtExistCheck=1, - pesize=pesize) - if not err: - err = self.storage.isMountPointInUse(lv) - - if err: - self.intf.messageWindow(_("Error With Request"), - "%s" % (err), custom_icon="error") - continue - if usedev.format.exists and format.mountable and \ self.storage.formatByDefault(usedev) and \ not queryNoFormatPreExisting(self.intf): diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index d1af32da0..bdaaacd22 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -176,15 +176,6 @@ class PartitionEditor: primary=primary, parents=disks) - err = storage.sanityCheckRequest(request) - if not err: - err = doUIRAIDLVMChecks(request, self.storage) - - if err: - self.intf.messageWindow(_("Error With Request"), - "%s" % (err), custom_icon="error") - continue - # we're all set, so create the actions if luksdev: actions.append(ActionCreateDevice(luksdev)) @@ -222,12 +213,6 @@ class PartitionEditor: if request.format.type != "none": actions.append(ActionResizeFormat(request, size)) - err = self.storage.sanityCheckRequest(request) - if err: - self.intf.messageWindow(_("Error With Request"), - "%s" % (err), custom_icon="error") - continue - if request.format.exists and \ getattr(request, "mountpoint", None) and \ self.storage.formatByDefault(request): diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index 8757bbc1e..900e45022 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -201,12 +201,6 @@ class RaidEditor: self.fsoptionsDict["migratecb"].get_active(): fstype = self.fsoptionsDict["migfstypeCombo"].get_active_value() - err = self.storage.sanityCheckRequest(request) - if err: - self.intf.messageWindow(_("Error With Request"), - "%s" % (err), custom_icon="error") - continue - if request.format.exists and \ self.storage.formatByDefault(request): if not queryNoFormatPreExisting(self.intf): -- cgit From 150eba0d3a55e316481bd89882456352925b74f0 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Mon, 2 Mar 2009 11:06:44 -1000 Subject: Handle existing volumes without labels. Existing volume may lack a label. If it's there, add it to the list, otherwise just go with the defaults. --- iw/partition_ui_helpers_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index ad7303e85..8fd455bc8 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -86,7 +86,7 @@ def createMountPointCombo(request, excludeMountPoints=[]): mntptlist = [] label = getattr(request.format, "label", None) - if request.exists and label.startswith("/"): + if request.exists and label and label.startswith("/"): mntptlist.append(label) idx = 0 -- cgit From 4d84ee5b4c551ddfd33427a75e2af804a0fda1db Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Mon, 2 Mar 2009 10:32:12 -1000 Subject: Add minSize and maxSize properties to StorageDevice and PartitionDevice. These properties take in to account the minimum and maximum sizes imposed by the device and partition type currently in use as well as the minimum and maximum sizes imposed by the DeviceFormat type on the device. --- storage/devices.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/storage/devices.py b/storage/devices.py index 4703fa5c1..d55f12046 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -476,6 +476,22 @@ class StorageDevice(Device): size = self._size return size + @property + def minSize(self): + """ The minimum size this device can be. """ + if self.format.minSize < self.size: + return self.format.minSize + else: + return size + + @property + def maxSize(self): + """ The maximum size this device can be. """ + if self.format.maxSize > self.currentSize: + return self.currentSize + else: + return self.format.maxSize + @property def status(self): """ This device's status. @@ -1029,6 +1045,17 @@ class PartitionDevice(StorageDevice): disk = property(lambda p: p._getDisk(), lambda p,d: p._setDisk(d)) + @property + def maxSize(self): + """ The maximum size this partition can be. """ + # XXX: this is MB by default + max = self.partedPartition.getMaxAvailableSize() + + if self.format.maxSize > max: + return max + else: + return self.format.maxSize + class DMDevice(StorageDevice): """ A device-mapper device """ -- cgit From e152e3cbff4607bc30e274a302a22dc98db86940 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Mon, 2 Mar 2009 11:07:35 -1000 Subject: Handle env vars in udev_device_get_lv_sizes() info['LVM2_LV_SIZE'] may contain some entries that are shell variable syntax, so strip the variable name and equal sign when building the sizes list. --- storage/udev.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/udev.py b/storage/udev.py index 4f83c2ad3..d849a0e5e 100644 --- a/storage/udev.py +++ b/storage/udev.py @@ -104,7 +104,7 @@ def udev_parse_block_entry(buf): elif tag == "S": dev['symlinks'].append(val) elif tag == "E": - if val.count("=") > 1 and val.count(" ") == 1: + if val.count("=") > 1 and val.count(" ") > 1: # eg: LVM2_LV_NAME when querying the VG for its LVs vars = val.split() vals = [] -- cgit From f0a87a7a9c8ff18de0d219aa38cf17dd67dd9958 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 2 Mar 2009 15:43:00 -0600 Subject: Fix check for multiple values in udev env lines. A line w/ two variable definitions can have a single space. --- storage/udev.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/udev.py b/storage/udev.py index d849a0e5e..6df00c855 100644 --- a/storage/udev.py +++ b/storage/udev.py @@ -104,7 +104,7 @@ def udev_parse_block_entry(buf): elif tag == "S": dev['symlinks'].append(val) elif tag == "E": - if val.count("=") > 1 and val.count(" ") > 1: + if val.count("=") > 1 and val.count(" ") > 0: # eg: LVM2_LV_NAME when querying the VG for its LVs vars = val.split() vals = [] -- cgit From 433598466bb359600dbb365484234c4bfcbc152a Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 2 Mar 2009 19:28:03 -0600 Subject: Import lvm module to provide has_lvm. --- iw/partition_gui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index 87d756f19..de2a201ea 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -45,6 +45,7 @@ from partIntfHelpers import * from constants import * from partition_ui_helpers_gui import * from storage.partitioning import doPartitioning +from storage.devicelibs import lvm import gettext _ = lambda x: gettext.ldgettext("anaconda", x) -- cgit From 0a2288ef7169f117c23de3f0641d0685efc24b63 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 2 Mar 2009 20:02:54 -0600 Subject: Expand newPartition and friends to accept any valid ctor args. Storage.newPartition, newVG, newLV, newMDArray all take variable argument lists, and pass along any unrecognized args to the constructor. --- storage/__init__.py | 95 +++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 70 insertions(+), 25 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index a2a95c679..beba77057 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -408,49 +408,94 @@ class Storage(object): def deviceDeps(self, device): return self.devicetree.getDependentDevices(device) - def newPartition(self, fmt_type=None, size=None, disks=[]): + def newPartition(self, *args, **kwargs): """ Return a new PartitionDevice instance for configuring. """ - return PartitionDevice("req%d" % self.nextID, - format=getFormat(fmt_type), - size=size, - parents=disks, - exists=False) + if kwargs.has_key("fmt_type"): + kwargs["format"] = getFormat(kwargs["fmt_type"]) + del kwargs["fmt_type"] - def newMDArray(self, fmt_type=None): + if kwargs.has_key("disks"): + parents = kwargs["disks"] + del kwargs["disks"] + + if kwargs.has_key("name"): + name = kwargs["name"] + del kwargs["name"] + else: + name = "req%d" % self.nextID + + return PartitionDevice(name, *args, **kwargs) + + def newMDArray(self, *args, **kwargs): """ Return a new MDRaidArrayDevice instance for configuring. """ - minor = self.unusedMDMinors[0] - return MDRaidArrayDevice("md%d" % minor, - minor=minor, - format=getFormat(fmt_type)) + if kwargs.has_key("fmt_type"): + kwargs["format"] = getFormat(kwargs["fmt_type"]) + del kwargs["fmt_type"] + + if kwargs.has_key("minor"): + minor = str(kwargs["minor"]) + del kwargs["minor"] + else: + kwargs["minor"] = str(self.unusedMDMinors[0]) - def newVG(self, name=None, pvs=[], peSize=None): + if kwargs.has_key("name"): + name = kwargs["name"] + del kwargs["name"] + else: + name = "md%s" % kwargs["minor"] + + return MDRaidArrayDevice(name, *args, **kwargs) + + def newVG(self, *args, **kwargs): """ Return a new LVMVolumeGroupDevice instance. """ + if kwargs.has_key("pvs"): + pvs = kwargs["pvs"] + del kwargs["pvs"] + for pv in pvs: - if not pv in self.devices: + if pv not in self.devices: raise ValueError("pv is not in the device tree") - if name and name in [d.name for d in self.devices]: - raise ValueError("name already in use") - elif not name: + if kwargs.has_key("name"): + name = kwargs["name"] + del kwargs["name"] + else: name = self.createSuggestedVGName(self.anaconda.id.network) - return LVMVolumeGroupDevice(name, parents=pvs, peSize=peSize) + if name in [d.name for d in self.devices]: + raise ValueError("name already in use") + + return LVMVolumeGroupDevice(name, pvs, *args, **kwargs) - def newLV(self, vg, name=None, fmt_type=None, size=None, - mountpoint=None): + def newLV(self, *args, **kwargs): """ Return a new LVMLogicalVolumeDevice instance. """ - if name and name in [d.name for d in self.devices]: - raise ValueError("name already in use") - elif not name: - if fmt_type == "swap": + if kwargs.has_key("vg"): + vg = kwargs["vg"] + del kwargs["vg"] + + mountpoint = kwargs.get("mountpoint") + kwargs.pop("mountpoint", None) + + if kwargs.has_key("fmt_type"): + kwargs["format"] = getFormat(kwargs["fmt_type"], mountpoint) + del kwargs["fmt_type"] + + if kwargs.has_key("name"): + name = kwargs["name"] + del kwargs["name"] + else: + if kwargs.get("format") and kwargs["format"].type == "swap": swap = True else: swap = False name = self.createSuggestedLVName(vg, swap=swap, mountpoint=mountpoint) - format = getFormat(fmt_type, mountpoint=mountpoint) - return LVMLogicalVolumeDevice(name, vg, format=format, size=size) + + if name in [d.name for d in self.devices]: + raise ValueError("name already in use") + + return LVMLogicalVolumeDevice(name, vg, *args, **kwargs) def createDevice(self, device): """ Schedule creation of a device. -- cgit From b8dcaee6959b8022e4a0ee155743c67f7d983a57 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 2 Mar 2009 20:10:30 -0600 Subject: Fix some bugs related to device setup/teardown usage by actions. --- storage/deviceaction.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/storage/deviceaction.py b/storage/deviceaction.py index 2f14a32b9..b76bb8fa3 100644 --- a/storage/deviceaction.py +++ b/storage/deviceaction.py @@ -223,6 +223,9 @@ class ActionResizeDevice(DeviceAction): if device.size == newsize: raise ValueError("new size same as old size") + if not device.resizable: + raise ValueError("device is not resizable") + DeviceAction.__init__(self, device) if newsize > device.currentSize: self.dir = RESIZE_GROW @@ -247,7 +250,8 @@ class ActionCreateFormat(DeviceAction): DeviceAction.__init__(self, device) if format: self.origFormat = device.format - self.device.format.teardown() + if self.device.format.exists: + self.device.format.teardown() self.device.format = format else: self.origFormat = getFormat(None) @@ -279,7 +283,7 @@ class ActionDestroyFormat(DeviceAction): def __init__(self, device): DeviceAction.__init__(self, device) self.origFormat = device.format - if device.format and device.format.status: + if device.format.exists: device.format.teardown() self.device.format = None @@ -319,6 +323,7 @@ class ActionResizeFormat(DeviceAction): self.device.format.targetSize = newsize def execute(self, intf=None): + self.device.setup() self.device.format.doResize(intf=intf) def cancel(self): @@ -337,6 +342,7 @@ class ActionMigrateFormat(DeviceAction): self.device.format.migrate = True def execute(self, intf=None): + self.device.setup() self.device.format.doMigrate(intf=intf) def cancel(self): -- cgit From ad0401b6317ce5516d78e0853f2f42887d10c9bb Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 2 Mar 2009 20:14:01 -0600 Subject: Make sure _name is None so _type gets used by name property. --- storage/formats/swap.py | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/formats/swap.py b/storage/formats/swap.py index 4740f3ae3..e5dd8ef3d 100644 --- a/storage/formats/swap.py +++ b/storage/formats/swap.py @@ -38,6 +38,7 @@ log = logging.getLogger("storage") class SwapSpace(DeviceFormat): """ Swap space """ _type = "swap" + _name = None _udevTypes = ["swap"] partedFlag = PARTITION_SWAP _formattable = True # can be formatted -- cgit From f681e13b55683311db51cf5d399141d60ed37c70 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 2 Mar 2009 23:21:26 -0600 Subject: Allow Storage.newVG to be called w/ no pvlist. --- storage/__init__.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/storage/__init__.py b/storage/__init__.py index beba77057..260ab8d0e 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -451,6 +451,8 @@ class Storage(object): if kwargs.has_key("pvs"): pvs = kwargs["pvs"] del kwargs["pvs"] + else: + pvs = [] for pv in pvs: if pv not in self.devices: -- cgit From 9fdbcd4b3304f5c7847a332687e58b35e75402d7 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 2 Mar 2009 23:23:56 -0600 Subject: Fix StorageDevice.minSize, PartitionDevice.targetSize, vg/lv type strings. --- storage/devices.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index d55f12046..68bda2173 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -479,10 +479,13 @@ class StorageDevice(Device): @property def minSize(self): """ The minimum size this device can be. """ + if self.exists: + self.setup() + if self.format.minSize < self.size: return self.format.minSize else: - return size + return self.size @property def maxSize(self): @@ -935,6 +938,7 @@ class PartitionDevice(StorageDevice): # this is in MB self._size = self.partedPartition.getSize() + self.targetSize = self._size self._partType = self.partedPartition.type @@ -1244,7 +1248,7 @@ class LVMVolumeGroupDevice(DMDevice): XXX Maybe this should inherit from StorageDevice instead of DMDevice since there's no actual device. """ - _type = "lvm vg" + _type = "lvmvg" def __init__(self, name, parents, size=None, free=None, peSize=None, peCount=None, peFree=None, pvCount=None, @@ -1568,7 +1572,7 @@ class LVMVolumeGroupDevice(DMDevice): class LVMLogicalVolumeDevice(DMDevice): """ An LVM Logical Volume """ - _type = "lvm lv" + _type = "lvmlv" _resizable = True def __init__(self, name, vgdev, size=None, uuid=None, -- cgit From 7bcb1e95d2dae520d11b444550dd41e009dd5bb8 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 2 Mar 2009 23:29:55 -0600 Subject: Add exclusiveDisks kwarg to doPartitioning, other minor fixes. - Check that we have a bootDev before checking if it exists. - Don't try to do anything in growPartitions unless there are some growable requests. --- storage/partitioning.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index dd31b81c8..a8ffc68a9 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -118,7 +118,8 @@ def doAutoPartition(anaconda): # run the autopart function to allocate and grow partitions try: - doPartitioning(anaconda) + doPartitioning(anaconda.id.storage, + exclusiveDisks=anaconda.id.storage.clearPartDisks) except PartitioningWarning as msg: if not anaconda.isKickstart: anaconda.intf.messageWindow(_("Warnings During Automatic " @@ -470,7 +471,7 @@ def getBestFreeSpaceRegion(disk, part_type, req_size, return best_free -def doPartitioning(storage): +def doPartitioning(storage, exclusiveDisks=None): """ Allocate and grow partitions. When this function returns without error, all PartitionDevice @@ -483,14 +484,21 @@ def doPartitioning(storage): storage - Main anaconda Storage instance + Keyword arguments: + + exclusiveDisks -- list of names of disks to use + """ anaconda = storage.anaconda - disks = [d for d in storage.disks if d.name in storage.clearPartDisks] + disks = storage.disks + if exclusiveDisks: + disks = [d for d in disks if d.name in exclusiveDisks] + partitions = storage.partitions # FIXME: isn't there a better place for this to happen? bootDev = anaconda.platform.bootDevice() - if not bootDev.exists: + if bootDev and not bootDev.exists: bootDev.req_bootable = True # FIXME: make sure non-existent partitions have empty parents list @@ -712,6 +720,8 @@ def growPartitions(disks, partitions): """ log.debug("growPartitions: disks=%s, partitions=%s" % ([d.name for d in disks], [p.name for p in partitions])) all_growable = [p for p in partitions if p.req_grow] + if not all_growable: + return # sort requests by base size in decreasing order all_growable.sort(key=lambda p: p.req_size, reverse=True) -- cgit From 2b36f31938a8613951a77e240fd1e11f758cce12 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 2 Mar 2009 23:49:49 -0600 Subject: Lots of little fixes to lvm dialogs. --- iw/lvm_dialog_gui.py | 111 ++++++++++++++++++++++++++++++++++----------------- 1 file changed, 75 insertions(+), 36 deletions(-) diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index 3aebb2032..a85fb7178 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -30,6 +30,7 @@ import gui from partition_ui_helpers_gui import * from constants import * from storage.devices import * +from storage.deviceaction import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -314,7 +315,7 @@ class VolumeGroupEditor: return True - def createAllowedLvmPartitionsList(self, alllvmparts, vgs, lvs): + def createAllowedLvmPartitionsList(self, alllvmparts, vgs): store = gtk.TreeStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_STRING) @@ -328,7 +329,7 @@ class VolumeGroupEditor: for device in alllvmparts: # clip size to current PE pesize = int(self.peCombo.get_active_value()) / 1024.0 - size = lvm.clampSize(size, pesize) + size = lvm.clampSize(device.size, pesize) size_string = "%10.2f MB" % size include = True selected = False @@ -404,7 +405,7 @@ class VolumeGroupEditor: row = 0 if lv.format.type == "luks": - usedev = self.storage.devicetree.getChildren(lv)[0] + usedev = self.findLUKSDev(lv) format = usedev.format elif lv: usedev = lv @@ -420,7 +421,7 @@ class VolumeGroupEditor: if not format.exists: lbl = createAlignedLabel(_("_File System Type:")) maintable.attach(lbl, 0, 1, row, row + 1) - newfstypeCombo = createFSTypeMenu(format.name, + newfstypeCombo = createFSTypeMenu(format, fstypechangeCB, mountCombo, ignorefs = ["software RAID", "physical volume (LVM)", "vfat", "efi", "PPC PReP Boot", "Apple Bootstrap"]) @@ -453,7 +454,7 @@ class VolumeGroupEditor: if lv and lv.lvname: lvnameEntry.set_text(lv.lvname) else: - lvnameEntry.set_text(storage.createSuggestedLVName(self.vg.lvs)) + lvnameEntry.set_text(storage.createSuggestedLVName(self.vg)) else: lbl = createAlignedLabel(_("Logical Volume Name:")) lvnameEntry = gtk.Label(lv.lvname) @@ -516,11 +517,11 @@ class VolumeGroupEditor: actions = [] luksdev = None targetSize = None - migrate_class = None + migrate = None format = None if lv.format.type == "luks": - usedev = self.storage.devicetree.getChildren(lv)[0] + usedev = self.findLUKSDev(lv) format = usedev.format else: usedev = lv @@ -631,22 +632,36 @@ class VolumeGroupEditor: # Ok -- now we've done all the checks to validate the # user-specified parameters. Time to set up the device... if not lv.exists: - lv.name = lvname + lv._name = lvname lv.size = size lv.req_grow = False format = fmt_class(mountpoint=mountpoint) if self.lukscb and self.lukscb.get_active() and \ - lv.format != "luks": + lv.format.type != "luks": luksformat = format format = getFormat("luks", passphrase=self.storage.encryptionPassphrase) luksdev = LUKSDevice("luks-%s" % lv.name, format=luksformat, parents=lv) + elif self.lukscb and not self.lukscb.get_active() and \ + lv.format.type == "luks": + # destroy luks format and mapped device + luksdev = self.findLUKSDev(lv) + if luksdev: + actions.append(ActionDestroyFormat(luksdev.format)) + actions.append(ActionDestroyDevice(luksdev)) + luksdev = None + actions.append(ActionDestroyFormat(lv)) + + if lv.lvname not in self.lvs: + actions.append(ActionCreateDevice(lv)) + actions.append(ActionCreateFormat(lv)) + self.lvs[lv.lvname] = lv - actions.append(ActionCreateDevice(lv)) if luksdev: actions.append(ActionCreateDevice(luksdev)) + actions.append(ActionCreateFormat(luksdev)) else: # existing lv if self.fsoptionsDict.has_key("formatcb") and \ @@ -661,13 +676,28 @@ class VolumeGroupEditor: luksdev = LUKSDevice("luks-%s" % lv.name, format=luksformat, parents=lv) + elif self.lukscb and not self.lukscb.get_active() and \ + lv.format.type == "luks": + # destroy luks format and mapped device + luksdev = self.findLUKSDev(lv) + if luksdev: + actions.append(ActionDestroyFormat(luksdev.format)) + actions.append(ActionDestroyDevice(luksdev)) + luksdev = None + actions.append(ActionDestroyFormat(lv)) + elif lv.format.mountable: + lv.format.mountpoint = mountpoint if self.fsoptionsDict.has_key("migratecb") and \ self.fsoptionsDict["migratecb"].get_active(): - migrate_class = self.fsoptionsDict["migfstypeCombo"].get_active_value() + migrate = True if self.fsoptionsDict.has_key("resizecb") and self.fsoptionsDict["resizecb"].get_active(): targetSize = self.fsoptionsDict["resizesb"].get_value_as_int() + actions.append(ActionResizeDevice(lv, targetSize)) + if lv.format.type: + actions.append(ActionResizeFormat(lv, targetSize)) + if format: actions.append(ActionDestroyFormat(usedev)) @@ -675,6 +705,9 @@ class VolumeGroupEditor: if luksdev: actions.append(ActionCreateDevice(luksdev)) + if migrate: + actions.append(ActionMigrateFormat(usedev)) + if usedev.format.exists and format.mountable and \ self.storage.formatByDefault(usedev) and \ not queryNoFormatPreExisting(self.intf): @@ -683,12 +716,7 @@ class VolumeGroupEditor: # everything ok break - for action in actions: - oldaction = self.storage.devicetree.registerAction(action) - if oldaction: - self.actions.remove(oldaction) - self.actions.append(action) - + self.actions.extend(actions) self.updateLogVolStore() self.updateVGSpaceLabels() dialog.destroy() @@ -701,10 +729,7 @@ class VolumeGroupEditor: return logvolname = self.logvolstore.get_value(iter, 0) - lv = self.storage.devicetree.getDeviceByName(logvolname) - if lv is None: - return - + lv = self.lvs[logvolname] self.editLogicalVolume(lv) def addLogicalVolumeCB(self, widget): @@ -752,16 +777,10 @@ class VolumeGroupEditor: if not rc: return - name = "%s-%s" % (self.vg.name, logvolname) - lv = self.storage.devicetree.getDeviceByName(name) + lv = self.lvs[logvolname] action = ActionDestroyDevice(lv) - oldaction = self.storage.devicetree.registerAction(action) - if oldaction: - self.actions.remove(oldaction) self.actions.append(action) - self.logvolstore.remove(iter) - self.updateVGSpaceLabels() return @@ -790,7 +809,7 @@ class VolumeGroupEditor: availSpaceMB = 0L for pv in pvlist: # XXX why the subtraction? - pvsize = lvm.clampSize(pe.size, curpe) - (curpe/1024) + pvsize = lvm.clampSize(pv.size, curpe) - (curpe/1024) # have to clamp pvsize to multiple of PE availSpaceMB = availSpaceMB + pvsize @@ -805,12 +824,27 @@ class VolumeGroupEditor: return neededSpaceMB + def findLUKSDev(self, lv): + if lv.format.type == "luks": + actions = self.actions[:] + actions.reverse() + usedev = None + for action in actions: + if action.isCreate() and action.isDevice() and \ + action.device.parents == [lv]: + usedev = action.device + break + if not usedev: + usedev = self.storage.devicetree.getChildren(lv)[0] + + return usedev + def updateLogVolStore(self): self.logvolstore.clear() for lv in self.vg.lvs: iter = self.logvolstore.append() if lv.format.type == "luks": - usedev = self.storage.devicetree.getChildren(lv)[0] + usedev = self.findLUKSDev(lv) else: usedev = lv @@ -856,18 +890,14 @@ class VolumeGroupEditor: # def run(self): if self.dialog is None: - return None + return [] while 1: rc = self.dialog.run() if rc == 2: self.destroy() - # cancel all the actions we have registered - self.actions.reverse() - for action in self.actions: - self.storage.devicetree.cancelAction(action) - return None + return [] pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model()) pesize = int(self.peCombo.get_active_value()) / 1024.0 @@ -912,6 +942,11 @@ class VolumeGroupEditor: # everything ok break + # pvs, pesize are taken care of in widget callbacks + # (clickCB, peChangeCB) + self.vg.name = volname + if self.isNew: + self.actions.insert(0, ActionCreateDevice(self.vg)) return self.actions def destroy(self): @@ -922,11 +957,15 @@ class VolumeGroupEditor: def __init__(self, anaconda, intf, parent, vg, isNew = 0): self.storage = anaconda.id.storage self.vg = vg + self.lvs = {} self.isNew = isNew self.intf = intf self.parent = parent self.actions = [] + for lv in self.vg.lvs: + self.lvs[lv.lvname] = lv + self.availlvmparts = self.storage.unusedPVs(vg=vg) # if no PV exist, raise an error message and return -- cgit From a7a885f7a0646a708541d88447cc866f54fe36cf Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 2 Mar 2009 23:51:45 -0600 Subject: Lots of fixes to partition dialog. - Always return a list from run(). - Handle the case of removing encryption from a device. - Schedule format create actions to go with device create actions. - Save mountpoint setting even when not formatting/creating. - pass format instance to createFSTypeMenu - create preexist options even for unknown formats --- iw/partition_dialog_gui.py | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index bdaaacd22..1c4c81d8d 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -27,7 +27,7 @@ import gtk import gui from storage.devices import PartitionDevice -from storage.deviceaction import ActionCreateFormat +from storage.deviceaction import * from partition_ui_helpers_gui import * from constants import * @@ -78,11 +78,11 @@ class PartitionEditor: # default to fixed, turn off max size spinbutton fillmaxszsb.set_sensitive(0) - if request.grow: - if request.maxSizeMB != None: + if request.req_grow: + if request.req_max_size: fillmaxszrb.set_active(1) fillmaxszsb.set_sensitive(1) - fillmaxszsb.set_value(request.maxSizeMB) + fillmaxszsb.set_value(request.req_max_size) else: fillunlimrb.set_active(1) else: @@ -99,16 +99,17 @@ class PartitionEditor: def run(self): if self.dialog is None: - return None + return [] while 1: rc = self.dialog.run() actions = [] + luksdev = None # user hit cancel, do nothing if rc == 2: self.destroy() - return None + return [] if not self.origrequest.exists: # read out UI into a partition specification @@ -130,6 +131,15 @@ class PartitionEditor: luksdev = LUKSDevice("luks%d" % self.storage.nextID, format=luksformat, parents=request) + elif self.lukscb and not self.lukscb.get_active() and \ + self.origrequest.format.type == "luks": + # destroy the luks format and the mapped device + luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] + if luksdev: + actions.append(ActionDestroyFormat(luksdev.format)) + actions.append(ActionDestroyDevice(luksdev)) + luksdev = None + actions.append(ActionDestroyFormat(self.origrequest)) if self.fixedrb.get_active(): grow = None @@ -179,7 +189,9 @@ class PartitionEditor: # we're all set, so create the actions if luksdev: actions.append(ActionCreateDevice(luksdev)) + actions.append(ActionCreateFormat(luksdev)) actions.append(ActionCreateDevice(request)) + actions.append(ActionCreateFormat(request)) else: # preexisting partition, just set mount point and format flag request = self.origrequest @@ -199,18 +211,18 @@ class PartitionEditor: device=self.origrequest.path, passphrase=self.storage.encryptionPassphrase) actions.append(ActionCreateFormat(request, format)) + elif request.format.mountable: + request.format.mountpoint = mountpoint if self.fsoptionsDict.has_key("migratecb") and \ self.fsoptionsDict["migratecb"].get_active(): - format = getFormat(self.fsoptionsDict["migfstypeCombo"].get_active_value(), mountpoint=mountpoint) - # TODO: implement ActionMigrateFormat - #actions.append(ActionMigrateFormat(request, format)) + actions.append(ActionMigrateFormat(request)) if self.fsoptionsDict.has_key("resizecb") and \ self.fsoptionsDict["resizecb"].get_active(): size = self.fsoptionsDict["resizesb"].get_value_as_int() actions.append(ActionResizeDevice(request, size)) - if request.format.type != "none": + if request.format.type: actions.append(ActionResizeFormat(request, size)) if request.format.exists and \ @@ -259,12 +271,9 @@ class PartitionEditor: # to make it seem like one device. wee! if self.origrequest.format.type == "luks": luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] - fmt_type = luksdev.format.type - mountpoint = getattr(luksdev.format, "mountpoint", None) - fslabel = getattr(luksdev.format, "label", None) - # XXX might need to add migrate stuff here, too usereq = luksdev else: + luksdev = None usereq = self.origrequest # Mount Point entry @@ -280,11 +289,7 @@ class PartitionEditor: lbl = createAlignedLabel(_("File System _Type:")) maintable.attach(lbl, 0, 1, row, row + 1) - if luksdev: - usereq = luksdev - else: - usereq = self.origrequest - self.newfstypeCombo = createFSTypeMenu(usereq.format.type, + self.newfstypeCombo = createFSTypeMenu(usereq.format, fstypechangeCB, self.mountCombo, availablefstypes = restrictfs) @@ -348,7 +353,6 @@ class PartitionEditor: # aren't protected (we'd still like to be able to mount them, though) self.fsoptionsDict = {} if self.origrequest.exists and \ - usereq.format.type != "none" and \ not self.storage.isProtected(self.origrequest): (row, self.fsoptionsDict) = createPreExistFSOptionSection(usereq, maintable, row, self.mountCombo, self.storage) -- cgit From f4dd93a995aaa206ce009ec840695e0af961c49a Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 2 Mar 2009 23:55:21 -0600 Subject: Lots of little fixes to raid dialog. - Always return a list from run(). - Handle removal of encryption. - Collect mountpoint even if not creating or formatting. - Collect migration/resize settings. - Pass format instance to createFSTypeMenu --- iw/raid_dialog_gui.py | 43 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 4 deletions(-) diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index 900e45022..df9501d68 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -29,6 +29,7 @@ import datacombo import gui from storage.devices import * +from storage.deviceaction import * from partition_ui_helpers_gui import * from constants import * @@ -131,7 +132,7 @@ class RaidEditor: def run(self): if self.dialog is None: - return None + return [] while 1: rc = self.dialog.run() @@ -139,11 +140,12 @@ class RaidEditor: # user hit cancel, do nothing if rc == 2: self.destroy() - return None + return [] actions = [] luksdev = None raidmembers = [] + migrate = None model = self.raidlist.get_model() iter = model.get_iter_first() while iter: @@ -180,6 +182,17 @@ class RaidEditor: parents=self.origrequest) format = getFormat("luks", passphrase=self.storage.encryptionPassphrase) + elif self.fsoptionsDict.has_key("lukscb") and \ + not self.fsoptionsDict["lukscb"].get_active() and \ + self.origrequest.format.type == "luks": + # destroy luks format and mapped device + luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] + if luksdev: + actions.append(ActionDestroyFormat(luksdev.format)) + actions.append(ActionDestroyDevice(luksdev)) + luksdev = None + actions.append(ActionDestroyFormat(request)) + else: # existing device fmt_class = self.fsoptionsDict["fstypeCombo"].get_active_value() @@ -196,10 +209,26 @@ class RaidEditor: format = getFormat("luks", device=self.origrequest.path, passphrase=self.storage.encryptionPassphrase) + elif self.fsoptionsDict.has_key("lukscb") and \ + not self.fsoptionsDict["lukscb"].get_active() and \ + request.format.type == "luks": + # destroy luks format and mapped device + luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] + if luksdev: + actions.append(ActionDestroyFormat(luksdev.format)) + actions.append(ActionDestroyDevice(luksdev)) + luksdev = None + actions.append(ActionDestroyFormat(request)) + elif request.format.mountable: + request.format.mountpoint = mountpoint if self.fsoptionsDict.has_key("migratecb") and \ self.fsoptionsDict["migratecb"].get_active(): - fstype = self.fsoptionsDict["migfstypeCombo"].get_active_value() + if origrequest.format.type == "luks": + usedev = self.storage.devicetree.getChildren(origrequest)[0] + else: + usedev = origrequest + migrate = True if request.format.exists and \ self.storage.formatByDefault(request): @@ -209,9 +238,15 @@ class RaidEditor: # everything ok, break out break + # FIXME: only create a new create action if the device isn't in the tree actions.append(ActionCreateDevice(self.origrequest)) + actions.append(ActionCreateFormat(self.origrequest)) if luksdev: actions.append(ActionCreateDevice(luksdev)) + actions.append(ActionCreateFormat(luksdev)) + + if migrate: + actions.append(ActionMigrateFormat(usedev)) return actions @@ -294,7 +329,7 @@ class RaidEditor: if not origrequest.exists: lbl = createAlignedLabel(_("_File System Type:")) maintable.attach(lbl, 0, 1, row, row + 1) - self.fstypeCombo = createFSTypeMenu(format.name, + self.fstypeCombo = createFSTypeMenu(format, fstypechangeCB, self.mountCombo, ignorefs = ["software RAID", "efi", "PPC PReP Boot", "Apple Bootstrap"]) -- cgit From d7a4ba3ff94cb0d9228ad20bd23b4be909350433 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 00:02:26 -0600 Subject: Several fixes to storage dialog common code. - Pass around format classes instead of fstype strings. - createFSTypeMenu now takes a format instance as its first arg. - Handle resize and migrate. --- iw/partition_ui_helpers_gui.py | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index 8fd455bc8..723367c9a 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -114,11 +114,11 @@ def createMountPointCombo(request, excludeMountPoints=[]): return mountCombo -def setMntPtComboStateFromType(fstype, mountCombo): - format = getFormat(fstype) +def setMntPtComboStateFromType(fmt_class, mountCombo): prevmountable = mountCombo.get_data("prevmountable") mountpoint = mountCombo.get_data("saved_mntpt") + format = fmt_class() if prevmountable and format.mountable: return @@ -180,18 +180,17 @@ def createAllowedDrivesList(disks, reqdrives, selectDrives=True, disallowDrives= # pass in callback for when fs changes because of python scope issues -def createFSTypeMenu(fstype, fstypechangeCB, mountCombo, +def createFSTypeMenu(format, fstypechangeCB, mountCombo, availablefstypes = None, ignorefs = None): store = gtk.TreeStore(gobject.TYPE_STRING, gobject.TYPE_PYOBJECT) fstypecombo = datacombo.DataComboBox(store) if availablefstypes: - names = [ availablefstypes ] + names = availablefstypes else: names = device_formats.keys() - fs = getFormat(fstype) - if fs and fs.supported and fs.formattable: - default = fstype + if format and format.supported and format.formattable: + default = format.name else: default = get_default_filesystem_type() @@ -199,7 +198,9 @@ def createFSTypeMenu(fstype, fstypechangeCB, mountCombo, defindex = 0 i = 0 for name in names: - format = getFormat(name) + # we could avoid instantiating them all if we made a static class + # method that does what the supported property does + format = device_formats[name]() if not format.supported: continue @@ -207,7 +208,7 @@ def createFSTypeMenu(fstype, fstypechangeCB, mountCombo, continue if format.formattable: - fstypecombo.append(name, device_formats[name]) + fstypecombo.append(format.name, device_formats[name]) if default == name: defindex = i defismountable = format.mountable @@ -293,11 +294,11 @@ def noformatCB(widget, data): def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, partitions, ignorefs=[]): rc = {} - ofstype = origrequest.format.type + ofstype = origrequest.format formatcb = gtk.CheckButton(label=_("_Format as:")) maintable.attach(formatcb, 0, 1, row, row + 1) - formatcb.set_active(istruefalse(origrequest.format.exists)) + formatcb.set_active(istruefalse(not origrequest.format.exists)) rc["formatcb"] = formatcb fstypeCombo = createFSTypeMenu(ofstype, fstypechangeCB, @@ -307,9 +308,8 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, row += 1 rc["fstypeCombo"] = fstypeCombo - # TODO: sort out fs migration if not formatcb.get_active() and not origrequest.format.migrate: - mountCombo.set_data("prevmountable", getFormat(ofstype).mountable) + mountCombo.set_data("prevmountable", origrequest.format.mountable) # this gets added to the table a bit later on lukscb = gtk.CheckButton(_("_Encrypt")) @@ -322,11 +322,11 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, migratecb = gtk.CheckButton(label=_("Mi_grate filesystem to:")) migratecb.set_active(istruefalse(origrequest.format.migrate)) - # TODO: unimplemented - migtypes = origrequest.format.migrationTarget + migtypes = [origrequest.format.migrationTarget] maintable.attach(migratecb, 0, 1, row, row + 1) - migfstypeCombo = createFSTypeMenu(ofstype, None, None, + migfstypeCombo = createFSTypeMenu(ofstype, + None, None, availablefstypes = migtypes) migfstypeCombo.set_sensitive(migratecb.get_active()) maintable.attach(migfstypeCombo, 1, 2, row, row + 1) @@ -341,7 +341,7 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, if origrequest.resizable: resizecb = gtk.CheckButton(label=_("_Resize")) - resizecb.set_active(origrequest.targetSize is not None) + resizecb.set_active(origrequest.targetSize != origrequest.currentSize) rc["resizecb"] = resizecb maintable.attach(resizecb, 0, 1, row, row + 1) @@ -350,7 +350,6 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, else: value = origrequest.size - # TODO: sort out resizing reqlower = origrequest.minSize requpper = origrequest.maxSize if origrequest.format.exists: -- cgit From 3579f254aa947d80b026d79482d1fd27f95ba180 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 00:06:09 -0600 Subject: Use currentSize in ActionResizeDevice ctor to get actual physical size. --- storage/deviceaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/deviceaction.py b/storage/deviceaction.py index b76bb8fa3..af263f0fa 100644 --- a/storage/deviceaction.py +++ b/storage/deviceaction.py @@ -220,7 +220,7 @@ class ActionResizeDevice(DeviceAction): obj = ACTION_OBJECT_DEVICE def __init__(self, device, newsize): - if device.size == newsize: + if device.currentSize == newsize: raise ValueError("new size same as old size") if not device.resizable: -- cgit From 1fd89ae9fc8e2ca44430702f7d988112a8a8c080 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 00:08:06 -0600 Subject: Several fixes to main partitioning gui. - Fix indentation of block that sets up format indicators for partitions. - Fix check for partitionable devices in deleteCB. - Refresh UI even if dialogs return empty action list, so we pick up little things like a newly set mountpoint on a pre- existing device. - Don't pass storage instance to vgeditor since anaconda has it. --- iw/partition_gui.py | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index de2a201ea..4466177ba 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -867,11 +867,10 @@ class PartitionWindow(InstallWindow): else: self.tree[iter]['Mount Point'] = "" - if device.format.type == "luks" and \ - not device.format.exists: - self.tree[iter]['Format'] = self.lock_pixbuf - elif not format.exists: - self.tree[iter]['Format'] = self.checkmark_pixbuf + if format and format.type == "luks" and not format.exists: + self.tree[iter]['Format'] = self.lock_pixbuf + elif format and not format.exists: + self.tree[iter]['Format'] = self.checkmark_pixbuf if format and format.type: self.tree[iter]['IsFormattable'] = device.format.formattable @@ -967,7 +966,7 @@ class PartitionWindow(InstallWindow): devices. This will need some work when that time comes. """ device = self.tree.getCurrentDevice() - if getattr(device.partedDisk): + if hasattr(device, "partedDisk"): if doDeleteDependentDevices(self.intf, self.storage, device): @@ -1069,9 +1068,6 @@ class PartitionWindow(InstallWindow): while 1: actions = raideditor.run() - if not actions: - break - for action in actions: # FIXME: this needs to handle exceptions self.storage.devicetree.registerAction(action) @@ -1100,9 +1096,6 @@ class PartitionWindow(InstallWindow): while 1: actions = parteditor.run() - if not actions: - break - for action in actions: # XXX we should handle exceptions here self.anaconda.id.storage.devicetree.registerAction(action) @@ -1128,7 +1121,6 @@ class PartitionWindow(InstallWindow): # we don't really need to pass in self.storage if we're passing # self.anaconda already vgeditor = lvm_dialog_gui.VolumeGroupEditor(self.anaconda, - self.storage, self.intf, self.parent, device, @@ -1137,9 +1129,6 @@ class PartitionWindow(InstallWindow): while True: actions = vgeditor.run() - if not actions: - break - for action in actions: # FIXME: handle exceptions self.storage.devicetree.registerAction(action) -- cgit From 96fecb7804f26db1e113f7fa9a1d4bacbb85fcc1 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 00:14:32 -0600 Subject: Add method pruneActions to remove redundant actions from the queue. At some point this can probably be triggered from registerAction, but for now this is simplest. The basic problem is that, when registering an action, you have to leave the device's previous action intact so the new action can be cancelled in the event of a failure like partition allocation. This leads to the possibility that lots of dialog clicking will lead to a very long sequence of actions that can/should be distilled to a minimal sequence. There could be problems lurking here with regard to device identity/equality. --- storage/devicetree.py | 162 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) diff --git a/storage/devicetree.py b/storage/devicetree.py index cdd41f070..b6fee30b1 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -152,6 +152,162 @@ class DeviceTree(object): self._populate() + def pruneActions(self): + """ Prune loops and redundant actions from the queue. """ + # handle device destroy actions + actions = self.findActions(type="destroy", object="device") + for a in actions: + if a not in self._actions: + # we may have removed some of the actions in a previous + # iteration of this loop + continue + + # XXX this may finally necessitate object ids + loops = self.findActions(device=a.device, + type="destroy", + object="device") + + if len(loops) == 1: + continue + + # remove all actions on this device from after the first + # destroy up through the last destroy + dev_actions = self.findActions(device=a.device) + for rem in dev_actions: + start = self._actions.index(a) + end = self._actions.index(loops[-1]) + if start < self._actions.index(rem) <= end: + self._actions.remove(rem) + + # device create actions + actions = self.findActions(type="create", object="device") + for a in actions: + if a not in self._actions: + # we may have removed some of the actions in a previous + # iteration of this loop + continue + + loops = self.findActions(device=a.device, + type="create", + object="device") + + if len(loops) == 1: + continue + + # remove all all actions on this device up to the last create + dev_actions = self.findActions(device=a.device) + for rem in dev_actions: + end = self._actions.index(loops[-1]) + if start < self._actions.index(rem) < end: + self._actions.remove(rem) + + # device resize actions + actions = self.findActions(type="resize", object="device") + for a in actions: + if a not in self._actions: + # we may have removed some of the actions in a previous + # iteration of this loop + continue + + loops = self.findActions(device=a.device, + type="resize", + object="device") + + if len(loops) == 1: + continue + + # remove all but the last resize action on this device + for rem in loops[:-1]: + self._actions.remove(rem) + + # format destroy + # XXX I don't think there's a way for these loops to happen + actions = self.findActions(type="destroy", object="format") + for a in actions: + if a not in self._actions: + # we may have removed some of the actions in a previous + # iteration of this loop + continue + + loops = self.findActions(device=a.device, + type="destroy", + object="format") + + if len(loops) == 1: + continue + + # remove all actions on this device's format from after the + # first destroy up through the last destroy + dev_actions = self.findActions(device=a.device, object="format") + for rem in dev_actions: + start = self._actions.index(a) + end = self._actions.index(loops[-1]) + if start < self._actions.index(rem) <= end: + self._actions.remove(rem) + + # format create + # XXX I don't think there's a way for these loops to happen + actions = self.findActions(type="create", object="format") + for a in actions: + if a not in self._actions: + # we may have removed some of the actions in a previous + # iteration of this loop + continue + + loops = self.findActions(device=a.device, + type="create", + object="format") + + if len(loops) == 1: + continue + + # remove all all actions on this device's format up to the last + # create + dev_actions = self.findActions(device=a.device, object="format") + for rem in dev_actions: + end = self._actions.index(loops[-1]) + if start < self._actions.index(rem) < end: + self._actions.remove(rem) + + # format resize + actions = self.findActions(type="resize", object="format") + for a in actions: + if a not in self._actions: + # we may have removed some of the actions in a previous + # iteration of this loop + continue + + loops = self.findActions(device=a.device, + type="resize", + object="format") + + if len(loops) == 1: + continue + + # remove all but the last resize action on this format + for rem in loops[:-1]: + self._actions.remove(rem) + + # format migrate + # XXX I don't think there's away for these loops to occur + actions = self.findActions(type="migrate", object="format") + for a in actions: + if a not in self._actions: + # we may have removed some of the actions in a previous + # iteration of this loop + continue + + loops = self.findActions(device=a.device, + type="migrate", + object="format") + + if len(loops) == 1: + continue + + # remove all but the last migrate action on this format + for rem in loops[:-1]: + self._actions.remove(rem) + def processActions(self, dryRun=None): """ Execute all registered actions. """ def cmpActions(x, y): @@ -255,6 +411,12 @@ class DeviceTree(object): # in most cases the actions will already be sorted because of the # rules for registration, but let's not rely on that + for action in self._actions: + log.debug("action: %s" % action) + log.debug("pruning action queue...") + self.pruneActions() + for action in self._actions: + log.debug("action: %s" % action) self._actions.sort(cmpActions) for action in self._actions: log.info("executing action: %s" % action) -- cgit From 90a8ecb9470e3583fbfea3125be914be8d36e2e7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 3 Mar 2009 09:22:35 +0100 Subject: Do not create PV's in empty space of disks not selected for install Do not create PV's in empty space of disks not selected for install. --- storage/partitioning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index a8ffc68a9..3331518c5 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -58,7 +58,7 @@ def doAutoPartition(anaconda): # get a list of disks that have at least one free space region of at # least 100MB disks = [] - for disk in anaconda.id.storage.disks: + for disk in [d for d in anaconda.id.storage.disks if d.name in anaconda.id.storage.clearPartDisks]: partedDisk = disk.partedDisk part = disk.partedDisk.getFirstPartition() while part: -- cgit From 2c82b2c4e0e0bbfa510dd5d5d26a31e0b20117da Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 3 Mar 2009 09:23:15 +0100 Subject: Maximize partitions after growing In some cases the grow code would not use the entire disk, this patch fixes this (and removes this item from the TODO list) --- storage/partitioning.py | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index 3331518c5..c10986533 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -707,11 +707,6 @@ def growPartitions(disks, partitions): this fails, we begin a rough binary search with a maximum of three iterations to settle on a new size. - TODO: Call disk.maximizePartition for each growable partition that - has not been allocated its full share of the free space upon - termination of each disk's loop iteration. Any detected - maximum size can be specified via a parted Constraint. - Arguments: disks -- a list of all usable disks (DiskDevice instances) @@ -840,9 +835,25 @@ def growPartitions(disks, partitions): except PartitioningError, e: raise PartitioningError("failed to grow partitions") - # TODO: call disk.maximizePartition with max_size as the - # constraint, in case it can grab some more free space + # Maximize partitions, we do this after growing all partitions + # as some partitions may grow unlimited, and we don't want them + # eating up the entire disk when we still need to grow others + for part in growable: + constraint = parted.Constraint(device=disk.partedDisk.device) + + # don't grow beyond the request's maximum size + if part.req_max_size: + max_sect = (part.req_max_size * (1024 * 1024)) / sectorSize + if constraint.max_size > max_sect: + constraint.max_size = max_sect + + # don't grow beyond the resident filesystem's max size + if part.format.maxSize > 0: + max_sect = (part.format.maxSize * (1024 * 1024)) / sectorSize + if constraint.max_size > max_sect: + constraint.max_size = max_sect + disk.partedDisk.maximizePartition(part.partedPartition, constraint) # reset all requests to their original requested size for part in partitions: -- cgit From 63ca139ca7da8a455904ec81cfc077ab662142ef Mon Sep 17 00:00:00 2001 From: Martin Sivak Date: Mon, 2 Mar 2009 12:53:24 +0100 Subject: Integrate the python-cryptsetup package --- anaconda.spec | 2 + storage/devicelibs/crypto.py | 158 ++++++++++--------------------------------- 2 files changed, 37 insertions(+), 123 deletions(-) diff --git a/anaconda.spec b/anaconda.spec index 6de4f6b88..8fc41a9f0 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -40,6 +40,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) %define createrepover 0.4.7 %define yumutilsver 1.1.11-3 %define iscsiver 6.2.0.870-3 +%define pythoncryptsetupver 0.0.4 BuildRequires: audit-libs-devel BuildRequires: booty @@ -109,6 +110,7 @@ Requires: authconfig Requires: gnome-python2-gtkhtml2 Requires: system-config-firewall Requires: cryptsetup-luks +Requires: python-cryptsetup >= %{pythoncryptsetupver} Requires: mdadm Requires: lvm2 Requires: util-linux-ng diff --git a/storage/devicelibs/crypto.py b/storage/devicelibs/crypto.py index d69e7d3ac..e16bbe4c5 100644 --- a/storage/devicelibs/crypto.py +++ b/storage/devicelibs/crypto.py @@ -17,9 +17,11 @@ # along with this program. If not, see . # # Author(s): Dave Lehman +# Martin Sivak # import os +from pycryptsetup import CryptSetup import iutil from ..errors import * @@ -27,169 +29,79 @@ from ..errors import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) +def askyes(question): + return True + +def dolog(priority, text): + pass + def is_luks(device): - rc = iutil.execWithRedirect("cryptsetup", - ["isLuks", device], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - searchPath = 1) - if rc: - return False - else: - return True + cs = CryptSetup(yesDialog = askyes, logFunc = dolog) + return cs.isLuks(device) def luks_uuid(device): - uuid = iutil.execWithCapture("cryptsetup", - ["luksUUID", device], - stderr="/dev/tty5") - return uuid.strip() + cs = CryptSetup(yesDialog = askyes, logFunc = dolog) + return cs.luksUUID(device).strip() def luks_status(name): - """0 means active, 1 means inactive (or non-existent)""" - rc = iutil.execWithRedirect("cryptsetup", - ["status", name], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - searchPath = 1) - return rc + """True means active, False means inactive (or non-existent)""" + cs = CryptSetup(yesDialog = askyes, logFunc = dolog) + return (cs.luksStatus(device)!=None) def luks_format(device, passphrase=None, key_file=None, cipher=None, key_size=None): - p = os.pipe() - argv = ["-q"] - os.close(p[1]) - - if cipher: - argv.extend(["--cipher", cipher]) + cs = CryptSetup(yesDialog = askyes, logFunc = dolog) + key_file_unlink = False - if key_size: - argv.append("--key-size=%d" % key_size) - - argv.extend(["luksFormat", device]) - if passphrase: - os.write(p[1], "%s\n" % passphrase) + key_file = cs.prepare_passphrase_file(passphrase) + key_file_unlink = True elif key_file and os.path.isfile(key_file): argv.append(key_file) else: raise ValueError("luks_format requires either a passphrase or a key file") - rc = iutil.execWithRedirect("cryptsetup", - argv, - stdin = p[0], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - searchPath = 1) + rc = cs.luksFormat(device = device, cipher = cipher, keysize = key_size, keyfile = key_file) + if key_file_unlink: os.unlink(key_file) - os.close(p[0]) if rc: raise CryptoError("luks_format failed for '%s'" % device) def luks_open(device, name, passphrase=None, key_file=None): - p = os.pipe() + cs = CryptSetup(yesDialog = askyes, logFunc = dolog) + key_file_unlink = False + if passphrase: - os.write(p[1], "%s\n" % passphrase) - argv = ["luksOpen", device, name] + key_file = cs.prepare_passphrase_file(passphrase) + key_file_unlink = True elif key_file and os.path.isfile(key_file): - argv = ["luksOpen", "--key-file", key_file, device, name] + pass else: raise ValueError("luks_open requires either a passphrase or a key file") - os.close(p[1]) - rc = iutil.execWithRedirect("cryptsetup", - argv, - stdin = p[0], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - searchPath = 1) - - os.close(p[0]) + rc = cs.luksOpen(device = device, name = name, keyfile = key_file) + if key_file_unlink: os.unlink(key_file) if rc: raise CryptoError("luks_open failed for %s (%s)" % (device, name)) def luks_close(name): - rc = iutil.execWithRedirect("cryptsetup", - ["luksClose", name], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - searchPath = 1) - + cs = CryptSetup(yesDialog = askyes, logFunc = dolog) + rc = cs.luksClose(name) if rc: raise CryptoError("luks_close failed for %s" % name) def luks_add_key(device, new_passphrase=None, new_key_file=None, passphrase=None, key_file=None): - p = os.pipe() - if passphrase: - os.write(p[1], "%s\n" % passphrase) - key_spec = "" - elif key_file and os.path.isfile(key_file): - key_spec = "--key-file %s" % key_file - else: - raise ValueError("luks_add_key requires either a passphrase or a key file") + cs = CryptSetup(yesDialog = askyes, logFunc = dolog) + return cs.addKey(device, new_passphrase, new_key_file, passphrase, key_file) - if new_passphrase: - os.write(p[1], "%s\n" % new_passphrase) - new_key_spec = "" - elif new_key_file and os.path.isfile(new_key_file): - new_key_spec = "%s" % new_key_file - else: - raise ValueError("luks_add_key requires either a passphrase or a key file to add") - - os.close(p[1]) - - rc = iutil.execWithRedirect("cryptsetup", - ["-q", - key_spec, - "luksAddKey", - device, - new_key_spec], - stdin = p[0], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - searchPath = 1) - - os.close(p[0]) - if rc: - raise CryptoError("luks add key failed") def luks_remove_key(device, del_passphrase=None, del_key_file=None, passphrase=None, key_file=None): - p = os.pipe() - if passphrase: - os.write(p[1], "%s\n" % passphrase) - key_spec = "" - elif key_file and os.path.isfile(key_file): - key_spec = "--key-file %s" % key_file - else: - raise ValueError("luks_remove_key requires either a passphrase or a key file") - - if del_passphrase: - os.write(p[1], "%s\n" % del_passphrase) - del_key_spec = "" - elif del_key_file and os.path.isfile(del_key_file): - del_key_spec = "%s" % del_key_file - else: - raise ValueError("luks_remove_key requires either a passphrase or a key file to remove") - - os.close(p[1]) - - rc = iutil.execWithRedirect("cryptsetup", - ["-q", - key_spec, - "luksRemoveKey", - device, - del_key_spec], - stdin = p[0], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - searchPath = 1) - - os.close(p[0]) - if rc: - raise CryptoError("luks_remove_key failed") + cs = CryptSetup(yesDialog = askyes, logFunc = dolog) + return cs.removeKey(device, del_passphrase, del_key_file, passphrase, key_file) -- cgit From b76fbf5ba2a3ce4db9726a51370121ac929fac6f Mon Sep 17 00:00:00 2001 From: Martin Sivak Date: Mon, 2 Mar 2009 15:32:44 +0100 Subject: Fix some remaining errors and require higher version of pycryptsetup --- anaconda.spec | 2 +- storage/devicelibs/crypto.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/anaconda.spec b/anaconda.spec index 8fc41a9f0..c581e907f 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -40,7 +40,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) %define createrepover 0.4.7 %define yumutilsver 1.1.11-3 %define iscsiver 6.2.0.870-3 -%define pythoncryptsetupver 0.0.4 +%define pythoncryptsetupver 0.0.6 BuildRequires: audit-libs-devel BuildRequires: booty diff --git a/storage/devicelibs/crypto.py b/storage/devicelibs/crypto.py index e16bbe4c5..be919ff89 100644 --- a/storage/devicelibs/crypto.py +++ b/storage/devicelibs/crypto.py @@ -46,7 +46,7 @@ def luks_uuid(device): def luks_status(name): """True means active, False means inactive (or non-existent)""" cs = CryptSetup(yesDialog = askyes, logFunc = dolog) - return (cs.luksStatus(device)!=None) + return cs.luksStatus(name)!=0 def luks_format(device, passphrase=None, key_file=None, @@ -58,7 +58,7 @@ def luks_format(device, key_file = cs.prepare_passphrase_file(passphrase) key_file_unlink = True elif key_file and os.path.isfile(key_file): - argv.append(key_file) + pass else: raise ValueError("luks_format requires either a passphrase or a key file") -- cgit From 4bd775a3fba650956a0f447b6b03070a84187658 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 12:14:57 -0600 Subject: Respect "xfs", "jfs", "icantbelieveitsnotbtr" cmdline args. --- storage/formats/fs.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index f80a877db..694b1f406 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -787,6 +787,15 @@ class BTRFS(FS): argv = ["-r", self.targetSize, self.device] return argv + @property + def supported(self): + """ Is this filesystem a supported type? """ + supported = self._supported + if flags.cmdline.has_key("icantbelieveitsnotbtr"): + supported = True + + return supported + register_device_format(BTRFS) @@ -829,6 +838,15 @@ class JFS(FS): _dump = True _check = True + @property + def supported(self): + """ Is this filesystem a supported type? """ + supported = self._supported + if flags.cmdline.has_key("jfs"): + supported = True + + return supported + register_device_format(JFS) -- cgit From 6e875cc2a061daf84d7dfd0dfc81658681447617 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 12:40:28 -0600 Subject: Don't compare with start when it is implicitly zero in pruneActions. --- storage/devicetree.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index b6fee30b1..7db11947b 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -198,7 +198,7 @@ class DeviceTree(object): dev_actions = self.findActions(device=a.device) for rem in dev_actions: end = self._actions.index(loops[-1]) - if start < self._actions.index(rem) < end: + if self._actions.index(rem) < end: self._actions.remove(rem) # device resize actions @@ -266,7 +266,7 @@ class DeviceTree(object): dev_actions = self.findActions(device=a.device, object="format") for rem in dev_actions: end = self._actions.index(loops[-1]) - if start < self._actions.index(rem) < end: + if self._actions.index(rem) < end: self._actions.remove(rem) # format resize -- cgit From 5aeeacd9b40935b4a89b9f90947eb31ecf5d5b12 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 12:41:58 -0600 Subject: LVM is giving us unacceptable grief over persistence of metadata. --- storage/formats/lvmpv.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/storage/formats/lvmpv.py b/storage/formats/lvmpv.py index a1c2e5322..cc243ebfe 100644 --- a/storage/formats/lvmpv.py +++ b/storage/formats/lvmpv.py @@ -87,6 +87,10 @@ class LVMPhysicalVolume(DeviceFormat): """ Consider use of -Z|--zero -f|--force or -y|--yes may be required """ + # lvm has issues with persistence of metadata, so here comes the + # hammer... + DeviceFormat.destroy(self, *args, **kwargs) + lvm.pvcreate(self.device) self.exists = True self.notifyKernel() -- cgit From b8f4081ed3df31d119dc049b7ab3498e29cdc8f9 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 14:15:16 -0600 Subject: Actually return the dict we built in FSSet.mountpoints. --- storage/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/__init__.py b/storage/__init__.py index 260ab8d0e..17b693fbb 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -916,6 +916,7 @@ class FSSet(object): for device in self.devices: if device.format.mountable and device.format.mountpoint: filesystems[device.format.mountpoint] = device + return filesystems def parseFSTab(self, chroot=""): """ parse /etc/fstab -- cgit From 5a30b2b2494136597d0eb757b6f4c601d4405124 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 14:20:58 -0600 Subject: Add size setter for LVs, remove obsolete comments about metadata sizes. --- storage/devices.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 68bda2173..32b220871 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -1535,19 +1535,21 @@ class LVMVolumeGroupDevice(DMDevice): # TODO: just ask lvm if isModified returns False # total the sizes of any LVs - # FIXME: need to account for metadata used = 0 + size = self.size + log.debug("%s size is %dMB" % (self.name, size)) for lv in self.lvs: + log.debug("lv %s (%s) uses %dMB" % (lv.name, lv, lv.size)) used += self.align(lv.size) - return self.size - used + free = self.size - used + log.debug("vg %s has %dMB free" % (self.name, free)) + return free @property def freeExtents(self): """ The number of free extents in this VG. """ # TODO: just ask lvm if isModified returns False - - # FIXME: need to account for metadata return self.freeSpace / self.peSize def align(self, size): @@ -1635,6 +1637,18 @@ class LVMLogicalVolumeDevice(DMDevice): """ raise NotImplementedError("probe method not defined for StorageDevice") + def _setSize(self, size): + size = self.vg.align(numeric_type(size)) + log.debug("trying to set lv %s size to %dMB" % (self.name, size)) + if size <= (self.vg.freeSpace + self._size): + self._size = size + self.targetSize = size + else: + log.debug("failed to set size: %dMB short" % (size - (self.vg.freeSpace + self._size),)) + raise ValueError("not enough free space in volume group") + + size = property(lambda d: d._size, _setSize) + @property def vg(self): """ This Logical Volume's Volume Group. """ -- cgit From 29c50520a8ca0a94f4e6a97b0169ca4d83a77f95 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 14:22:52 -0600 Subject: parted.Constraint's max size is maxSize, not max_size. --- storage/partitioning.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index c10986533..c9522d40c 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -844,14 +844,14 @@ def growPartitions(disks, partitions): # don't grow beyond the request's maximum size if part.req_max_size: max_sect = (part.req_max_size * (1024 * 1024)) / sectorSize - if constraint.max_size > max_sect: - constraint.max_size = max_sect + if constraint.maxSize > max_sect: + constraint.maxSize = max_sect # don't grow beyond the resident filesystem's max size if part.format.maxSize > 0: max_sect = (part.format.maxSize * (1024 * 1024)) / sectorSize - if constraint.max_size > max_sect: - constraint.max_size = max_sect + if constraint.maxSize > max_sect: + constraint.maxSize = max_sect disk.partedDisk.maximizePartition(part.partedPartition, constraint) -- cgit From c03a758c62317db66c51e7aefb98030806c67698 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 15:34:55 -0600 Subject: Fix up growLVM to make full use of the VGs' space. --- storage/partitioning.py | 114 +++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 99 insertions(+), 15 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index c9522d40c..7160bb3c5 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -861,6 +861,37 @@ def growPartitions(disks, partitions): continue part.req_size = part.req_base_size +def lvCompare(lv1, lv2): + """ More specifically defined lvs come first. + + < 1 => x < y + 0 => x == y + > 1 => x > y + """ + ret = 0 + + # larger requests go to the front of the list + ret -= cmp(lv1.size, lv2.size) * 100 + + # fixed size requests to the front + ret += cmp(lv1.req_grow, lv2.req_grow) * 50 + + # potentially larger growable requests go to the front + if lv1.req_grow and lv2.req_grow: + if not lv1.req_max_size and lv2.req_max_size: + ret -= 25 + elif lv1.req_max_size and not lv2.req_max_size: + ret += 25 + else: + ret -= cmp(lv1.req_max_size, lv2.req_max_size) * 25 + + if ret > 0: + ret = 1 + elif ret < 0: + ret = -1 + + return ret + def growLVM(storage): """ Grow LVs according to the sizes of the PVs. """ for vg in storage.vgs: @@ -869,44 +900,77 @@ def growLVM(storage): log.debug("vg %s has no free space" % vg.name) continue + log.debug("vg %s: %dMB free ; lvs: %s" % (vg.name, vg.freeSpace, + [l.lvname for l in vg.lvs])) + # figure out how much to grow each LV grow_amounts = {} lv_total = vg.size - total_free + log.debug("used: %dMB ; vg.size: %dMB" % (lv_total, vg.size)) # This first loop is to calculate percentage-based growth # amounts. These are based on total free space. + lvs = vg.lvs + lvs.sort(cmp=lvCompare) for lv in lvs: if not lv.req_grow or not lv.req_percent: continue - portion = (lv_req_percent * 0.01) - # clamp growth amount to a multiple of vg extent size - grow = vg.align(portion * vg.vgFree) + portion = (lv.req_percent * 0.01) + grow = portion * vg.vgFree new_size = lv.req_size + grow if lv.req_max_size and new_size > lv.req_max_size: - # clamp growth amount to a multiple of vg extent size - grow -= align(new_size - lv.req_max_size) + grow -= (new_size - lv.req_max_size) + + if lv.format.maxSize and lv.format.maxSize < new_size: + grow -= (new_size - lv.format.maxSize) # clamp growth amount to a multiple of vg extent size grow_amounts[lv.name] = vg.align(grow) total_free -= grow + lv_total += grow # This second loop is to calculate non-percentage-based growth # amounts. These are based on free space remaining after # calculating percentage-based growth amounts. + + # keep a tab on space not allocated due to format or requested + # maximums -- we'll dole it out to subsequent requests + leftover = 0 for lv in lvs: + log.debug("checking lv %s: req_grow: %s ; req_percent: %s" + % (lv.name, lv.req_grow, lv.req_percent)) if not lv.req_grow or lv.req_percent: continue portion = float(lv.req_size) / float(lv_total) - # clamp growth amount to a multiple of vg extent size - grow = vg.align(portion * total_free) - new_size = lv.req_size + grow - if lv.req_max_size and new_size > lv.req_max_size: - # clamp growth amount to a multiple of vg extent size - grow -= vg.align(new_size - lv.req_max_size) - - grow_amounts[lv.name] = grow + grow = portion * total_free + log.debug("grow is %dMB" % grow) + + todo = lvs[lvs.index(lv):] + unallocated = reduce(lambda x,y: x+y, + [l.req_size for l in todo + if l.req_grow and not l.req_percent]) + extra_portion = float(lv.req_size) / float(unallocated) + extra = extra_portion * leftover + log.debug("%s getting %dMB (%d%%) of %dMB leftover space" + % (lv.name, extra, extra_portion * 100, leftover)) + leftover -= extra + grow += extra + log.debug("grow is now %dMB" % grow) + max_size = lv.req_size + grow + if lv.req_max_size and max_size > lv.req_max_size: + max_size = lv.req_max_size + + if lv.format.maxSize and max_size > lv.format.maxSize: + max_size = lv.format.maxSize + + log.debug("max size is %dMB" % max_size) + max_size = max_size + leftover += (lv.req_size + grow) - max_size + grow = max_size - lv.req_size + log.debug("lv %s gets %dMB" % (lv.name, vg.align(grow))) + grow_amounts[lv.name] = vg.align(grow) if not grow_amounts: log.debug("no growable lvs in vg %s" % vg.name) @@ -916,12 +980,32 @@ def growLVM(storage): for lv in lvs: if lv.name not in grow_amounts.keys(): continue - lv.size = new_size + lv.size += grow_amounts[lv.name] # now there shouldn't be any free space left, but if there is we # should allocate it to one of the LVs vg_free = vg.freeSpace - log.debug("vg %s still has %dMB free" % (vg.name, vg_free)) + log.debug("vg %s has %dMB free" % (vg.name, vg_free)) + if vg_free: + for lv in lvs: + if not lv.req_grow: + continue + + if lv.req_max_size and lv.size == lv.req_max_size: + continue + + if lv.format.maxSize and lv.size == lv.format.maxSize: + continue + + # first come, first served + projected = lv.size + vg.freeSpace + if lv.req_max_size and projected > lv.req_max_size: + projected = lv.req_max_size + if lv.format.maxSize and projected > lv.format.maxSize: + projected = lv.format.maxSize + log.debug("giving leftover %dMB to %s" % (projected - lv.size, + lv.name)) + lv.size = projected -- cgit From 413ed11723371780c902182886f783a89e561123 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 15:36:04 -0600 Subject: Don't try to fool around with disks that have no free space. --- storage/partitioning.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/storage/partitioning.py b/storage/partitioning.py index 7160bb3c5..735a45f1f 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -728,6 +728,10 @@ def growPartitions(disks, partitions): sectorSize = disk.partedDisk.device.physicalSectorSize # get a list of free space regions on the disk free = disk.partedDisk.getFreeSpaceRegions() + if not free: + log.debug("no free space on %s" % disk.name) + continue + # sort the free regions in decreasing order of size free.sort(key=lambda r: r.length, reverse=True) disk_free = reduce(lambda x,y: x + y, [f.length for f in free]) -- cgit From d1e504f41da2f102a24c6fa9fa344c85c64d718e Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 19:50:19 -0600 Subject: More work on iutil.execWith function file handling. This time I've tested them fairly well, so hopefully these changes will be the last for a while. --- iutil.py | 74 +++++++++++++++++++++++++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 29 deletions(-) diff --git a/iutil.py b/iutil.py index 2df995a7d..b1114f4db 100644 --- a/iutil.py +++ b/iutil.py @@ -52,25 +52,31 @@ def execWithRedirect(command, argv, stdin = None, stdout = None, argv = list(argv) if isinstance(stdin, str): if os.access(stdin, os.R_OK): - stdin = open(stdin) + stdin = os.open(stdin, os.O_RDONLY) else: - stdin = sys.stdin + stdin = sys.stdin.fileno() + elif isinstance(stdin, int): + pass elif stdin is None or not isinstance(stdin, file): - stdin = sys.stdin + stdin = sys.stdin.fileno() if isinstance(stdout, str): - stdout = open(stdout, "w") + stdout = os.open(stdout, os.O_RDWR) + elif isinstance(stdout, int): + pass elif stdout is None or not isinstance(stdout, file): - stdout = sys.stdout + stdout = sys.stdout.fileno() if isinstance(stderr, str): - stderr = open(stderr, "w") + stderr = os.open(stderr, os.O_RDWR) + elif isinstance(stderr, int): + pass elif stderr is None or not isinstance(stderr, file): - stderr = sys.stderr + stderr = sys.stderr.fileno() runningLog = open("/tmp/program.log", "a") runningLog.write("Running... %s\n" % ([command] + argv,)) - stdout.write("Running... %s\n" %([command] + argv,)) + os.write(stdout, "Running... %s\n" %([command] + argv,)) try: proc = subprocess.Popen([command] + argv, stdin=stdin, @@ -81,10 +87,10 @@ def execWithRedirect(command, argv, stdin = None, stdout = None, while True: (outStr, errStr) = proc.communicate() if outStr: - stdout.write(outStr) + os.write(stdout, outStr) runningLog.write(outStr) if errStr: - stderr.write(errStr) + os.write(stderr, errStr) runningLog.write(errStr) if proc.returncode is not None: @@ -116,16 +122,20 @@ def execWithCapture(command, argv, stdin = None, stderr = None, root='/'): if isinstance(stdin, str): if os.access(stdin, os.R_OK): - stdin = open(stdin) + stdin = os.open(stdin, os.O_RDONLY) else: - stdin = sys.stdin + stdin = sys.stdin.fileno() + elif isinstance(stdin, int): + pass elif stdin is None or not isinstance(stdin, file): - stdin = sys.stdin + stdin = sys.stdin.fileno() if isinstance(stderr, str): - stderr = open(stderr, "w") + stderr = os.open(stderr, os.O_RDWR) + elif isinstance(stderr, int): + pass elif stderr is None or not isinstance(stderr, file): - stderr = sys.stderr + stderr = sys.stderr.fileno() runningLog = open("/tmp/program.log", "a") runningLog.write("Running... %s\n" % ([command] + argv,)) @@ -143,7 +153,7 @@ def execWithCapture(command, argv, stdin = None, stderr = None, root='/'): rc += outStr if errStr: runningLog.write(errStr) - stderr.write(errStr) + os.write(stderr, errStr) if proc.returncode is not None: break @@ -161,34 +171,40 @@ def execWithPulseProgress(command, argv, stdin = None, stdout = None, argv = list(argv) if isinstance(stdin, str): if os.access(stdin, os.R_OK): - stdin = open(stdin) + stdin = os.open(stdin, os.O_RDONLY) else: - stdin = sys.stdin + stdin = sys.stdin.fileno() + elif isinstance(stdin, int): + pass elif stdin is None or not isinstance(stdin, file): - stdin = sys.stdin + stdin = sys.stdin.fileno() if isinstance(stdout, str): - stdout = open(stdout, "w") + stdout = os.open(stdout, os.O_RDWR) + elif isinstance(stdout, int): + pass elif stdout is None or not isinstance(stdout, file): - stdout = sys.stdout + stdout = sys.stdout.fileno() if isinstance(stderr, str): - stderr = open(stderr, "w") + stderr = os.open(stderr, os.O_RDWR) + elif isinstance(stderr, int): + pass elif stderr is None or not isinstance(stderr, file): - stderr = sys.stderr + stderr = sys.stderr.fileno() - stdout.write("Running... %s\n" %([command] + argv,)) + os.write(stdout, "Running... %s\n" %([command] + argv,)) p = os.pipe() childpid = os.fork() if not childpid: os.close(p[0]) os.dup2(p[1], 1) - os.dup2(stderr.fileno(), 2) - os.dup2(stdin.fileno(), 0) - stdin.close() + os.dup2(stderr, 2) + os.dup2(stdin, 0) + os.close(stdin) os.close(p[1]) - stderr.close() + os.close(stderr) os.execvp(command, [command] + argv) os._exit(1) @@ -203,7 +219,7 @@ def execWithPulseProgress(command, argv, stdin = None, stdout = None, if (num != 4): raise IOError, args - stdout.write(s) + os.write(stdout, s) if progress: progress.pulse() if len(s) < 1: -- cgit From a054ef05af6a1e3f7dc25397701982bb27859f5c Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 19:51:58 -0600 Subject: Import LUKSDevice from storage.devices since we might use it. --- iw/partition_dialog_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index 1c4c81d8d..04c49e3a7 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -26,7 +26,7 @@ import gobject import gtk import gui -from storage.devices import PartitionDevice +from storage.devices import PartitionDevice, LUKSDevice from storage.deviceaction import * from partition_ui_helpers_gui import * from constants import * -- cgit From c6d872a2858e9c5db67b6bc9749ffd6e6026de63 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 19:52:34 -0600 Subject: Fix check for newly encrypted partitions in main part window. --- iw/partition_gui.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index 4466177ba..c704b0893 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -867,7 +867,9 @@ class PartitionWindow(InstallWindow): else: self.tree[iter]['Mount Point'] = "" - if format and format.type == "luks" and not format.exists: + if device and device.format and \ + device.format.type == "luks" and \ + not device.format.exists: self.tree[iter]['Format'] = self.lock_pixbuf elif format and not format.exists: self.tree[iter]['Format'] = self.checkmark_pixbuf -- cgit From 4b862f6b31575e223b9a7d4f44f3b7752dc7d35b Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 19:53:50 -0600 Subject: Make sure we don't leave things active that we're removing. --- storage/deviceaction.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/storage/deviceaction.py b/storage/deviceaction.py index af263f0fa..462fc0937 100644 --- a/storage/deviceaction.py +++ b/storage/deviceaction.py @@ -209,6 +209,8 @@ class ActionDestroyDevice(DeviceAction): def __init__(self, device): # XXX should we insist that device.fs be None? DeviceAction.__init__(self, device) + if device.exists: + device.teardown() def execute(self, intf=None): self.device.destroy() @@ -292,6 +294,7 @@ class ActionDestroyFormat(DeviceAction): if self.origFormat: self.device.setup() self.origFormat.destroy() + self.device.teardown() def cancel(self): self.device.format = self.origFormat @@ -348,5 +351,3 @@ class ActionMigrateFormat(DeviceAction): def cancel(self): self.device.format.migrate = False - - -- cgit From 47e5e0a61c5edfdd44cfb2fca3d7528f422594a0 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 19:54:23 -0600 Subject: Size fixup for LUKS header and a destroy method for LUKSDevice. --- storage/devices.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/storage/devices.py b/storage/devices.py index 32b220871..030d2ec07 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -1200,6 +1200,11 @@ class LUKSDevice(DMCryptDevice): parents=parents, sysfsPath=sysfsPath, uuid=None, exists=exists) + @property + def size(self): + # break off 2KB for the LUKS header + return float(self.slave.size) - (2.0 / 1024) + def create(self, intf=None): """ Create the device. """ log_method_call(self, self.name, status=self.status) @@ -1211,6 +1216,7 @@ class LUKSDevice(DMCryptDevice): #if not self.slave.format.exists: # self.slave.format.create() + self._name = self.slave.format.mapName self.exists = True self.setup() @@ -1236,6 +1242,11 @@ class LUKSDevice(DMCryptDevice): if recursive: self.teardownParents(recursive=recursive) + def destroy(self): + log_method_call(self, self.name, status=self.status) + self.format.teardown() + self.teardown() + @property def slave(self): """ This device's backing device. """ -- cgit From 535c7454514b686319110d8fe615966e5c262fc8 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 20:00:22 -0600 Subject: Separate checks for mapName and key/passphrase, add a destroy method. Also, set the mapName immediately after formatting since that's the earliest we can possibly have the UUID. --- storage/formats/luks.py | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/storage/formats/luks.py b/storage/formats/luks.py index a2b3b62d3..a4a20d697 100644 --- a/storage/formats/luks.py +++ b/storage/formats/luks.py @@ -79,18 +79,15 @@ class LUKS(DeviceFormat): passphrase = property(fset=_setPassphrase) + @property + def hasKey(self): + return (self.__passphrase or + (self._key_file and os.access(self._key_file, os.R_OK))) + @property def configured(self): """ To be ready we need a key or passphrase and a map name. """ - ready = True - if not self.__passphrase or \ - (self._key_file and os.access(self._key_file, os.R_OK)): - ready = False - - if not self.mapName: - ready = False - - return ready + return self.hasKey and self.mapName @property def status(self): @@ -107,7 +104,7 @@ class LUKS(DeviceFormat): def setup(self, *args, **kwargs): """ Open, or set up, the format. """ - log_method_call(self, device=self.device, + log_method_call(self, device=self.device, mapName=self.mapName, type=self.type, status=self.status) if not self.configured: raise LUKSError("luks device not configured") @@ -135,8 +132,8 @@ class LUKS(DeviceFormat): """ Create the format. """ log_method_call(self, device=self.device, type=self.type, status=self.status) - if not self.configured: - raise LUKSError("luks device not configured") + if not self.hasKey: + raise LUKSError("luks device has no key/passphrase") DeviceFormat.create(self, *args, **kwargs) crypto.luks_format(self.device, @@ -147,8 +144,16 @@ class LUKS(DeviceFormat): self.uuid = crypto.luks_uuid(self.device) self.exists = True + self.mapName = "luks-%s" % self.uuid self.notifyKernel() + def destroy(self, *args, **kwargs): + """ Create the format. """ + log_method_call(self, device=self.device, + type=self.type, status=self.status) + self.teardown() + DeviceFormat.destroy(self, *args, **kwargs) + @property def keyFile(self): """ Path to key file to be used in /etc/crypttab """ -- cgit From 79a294a576aa56fb1a951e0e4f1488507e84665d Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 20:04:47 -0600 Subject: Add support for encrypted autopart and use expanded Storage.newFoo methods. --- storage/partitioning.py | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index 735a45f1f..697fe0629 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -32,7 +32,7 @@ from constants import * from errors import * from deviceaction import * -from devices import PartitionDevice +from devices import PartitionDevice, LUKSDevice import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -73,14 +73,18 @@ def doAutoPartition(anaconda): part = part.nextPartition() # create a separate pv partition for each disk with free space - pvs = [] + devs = [] for disk in disks: - part = anaconda.id.storage.newPartition(fmt_type="lvmpv", + if anaconda.id.storage.encryptedAutoPart: + fmt_type = "luks" + else: + fmt_type = "lvmpv" + part = anaconda.id.storage.newPartition(fmt_type=fmt_type, size=1, + grow=True, disks=[disk]) - part.req_grow = True anaconda.id.storage.createDevice(part) - pvs.append(part) + devs.append(part) # # Convert storage.autoPartitionRequests into Device instances and @@ -98,16 +102,11 @@ def doAutoPartition(anaconda): dev = anaconda.id.storage.newPartition(fmt_type=fstype, size=size, + grow=grow, + maxsize=maxsize, + mountpoint=mountpoint, disks=disks) - if grow: - dev.req_grow = True - if maxsize: - dev.req_max_size = maxsize - - if mountpoint: - dev.format.mountpoint = mountpoint - # schedule the device for creation anaconda.id.storage.createDevice(dev) @@ -144,6 +143,18 @@ def doAutoPartition(anaconda): if anaconda.isKickstart: sys.exit(0) + if anaconda.id.storage.encryptedAutoPart: + pvs = [] + for dev in devs: + pv = LUKSDevice("luks-%s" % dev.name, + format=getFormat("lvmpv", device=dev.path), + size=dev.size, + parents=dev) + pvs.append(pv) + anaconda.id.storage.createDevice(pv) + else: + pvs = devs + # create a vg containing all of the autopart pvs vg = anaconda.id.storage.newVG(pvs=pvs) anaconda.id.storage.createDevice(vg) @@ -165,16 +176,10 @@ def doAutoPartition(anaconda): dev = anaconda.id.storage.newLV(vg=vg, fmt_type=fstype, mountpoint=mountpoint, + grow=grow, + maxsize=maxsize, size=size) - if grow: - dev.req_grow = True - if maxsize: - dev.req_max_size = maxsize - - if mountpoint: - dev.format.mountpoint = mountpoint - # schedule the device for creation anaconda.id.storage.createDevice(dev) -- cgit From df6752a52a26f0ffdf0a0d4ad14653f1660cec45 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 20:10:45 -0600 Subject: Fix various bugs in and clean up Storage.new{LV,VG,MDArray,Partition}. --- storage/__init__.py | 49 ++++++++++++++++++------------------------------- 1 file changed, 18 insertions(+), 31 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 17b693fbb..575ecd16f 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -411,16 +411,15 @@ class Storage(object): def newPartition(self, *args, **kwargs): """ Return a new PartitionDevice instance for configuring. """ if kwargs.has_key("fmt_type"): - kwargs["format"] = getFormat(kwargs["fmt_type"]) - del kwargs["fmt_type"] + kwargs["format"] = getFormat(kwargs.pop("fmt_type"), + mountpoint=kwargs.pop("mountpoint", + None)) if kwargs.has_key("disks"): - parents = kwargs["disks"] - del kwargs["disks"] + parents = kwargs.pop("disks") if kwargs.has_key("name"): - name = kwargs["name"] - del kwargs["name"] + name = kwargs.pop("name") else: name = "req%d" % self.nextID @@ -429,18 +428,17 @@ class Storage(object): def newMDArray(self, *args, **kwargs): """ Return a new MDRaidArrayDevice instance for configuring. """ if kwargs.has_key("fmt_type"): - kwargs["format"] = getFormat(kwargs["fmt_type"]) - del kwargs["fmt_type"] + kwargs["format"] = getFormat(kwargs.pop("fmt_type"), + mountpoint=kwargs.pop("mountpoint", + None)) if kwargs.has_key("minor"): - minor = str(kwargs["minor"]) - del kwargs["minor"] + minor = str(kwargs.pop("minor")) else: kwargs["minor"] = str(self.unusedMDMinors[0]) if kwargs.has_key("name"): - name = kwargs["name"] - del kwargs["name"] + name = kwargs.pop("name") else: name = "md%s" % kwargs["minor"] @@ -448,19 +446,13 @@ class Storage(object): def newVG(self, *args, **kwargs): """ Return a new LVMVolumeGroupDevice instance. """ - if kwargs.has_key("pvs"): - pvs = kwargs["pvs"] - del kwargs["pvs"] - else: - pvs = [] - + pvs = kwargs.pop("pvs", []) for pv in pvs: if pv not in self.devices: raise ValueError("pv is not in the device tree") if kwargs.has_key("name"): - name = kwargs["name"] - del kwargs["name"] + name = kwargs.pop("name") else: name = self.createSuggestedVGName(self.anaconda.id.network) @@ -472,19 +464,15 @@ class Storage(object): def newLV(self, *args, **kwargs): """ Return a new LVMLogicalVolumeDevice instance. """ if kwargs.has_key("vg"): - vg = kwargs["vg"] - del kwargs["vg"] - - mountpoint = kwargs.get("mountpoint") - kwargs.pop("mountpoint", None) + vg = kwargs.pop("vg") + mountpoint = kwargs.pop("mountpoint", None) if kwargs.has_key("fmt_type"): - kwargs["format"] = getFormat(kwargs["fmt_type"], mountpoint) - del kwargs["fmt_type"] + kwargs["format"] = getFormat(kwargs.pop("fmt_type"), + mountpoint=mountpoint) if kwargs.has_key("name"): - name = kwargs["name"] - del kwargs["name"] + name = kwargs.pop("name") else: if kwargs.get("format") and kwargs["format"].type == "swap": swap = True @@ -505,8 +493,7 @@ class Storage(object): TODO: We could do some things here like assign the next available raid minor if one isn't already set. """ - action = ActionCreateDevice(device) - self.devicetree.registerAction(action) + self.devicetree.registerAction(ActionCreateDevice(device)) if device.format.type: self.devicetree.registerAction(ActionCreateFormat(device)) -- cgit From c3e1cf577ec3caf2dca1c536ec0b1e8e25edb222 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 20:12:59 -0600 Subject: Add passphrase entry machinery for encrypted devices. --- storage/__init__.py | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/storage/__init__.py b/storage/__init__.py index 575ecd16f..11e29d562 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -74,6 +74,41 @@ def storageComplete(anaconda): sys.exit(0) return DISPATCH_FORWARD + devs = anaconda.id.storage.devicetree.getDevicesByType("luks/dm-crypt") + existing_luks = False + new_luks = False + for dev in devs: + if dev.exists: + existing_luks = True + break + if (anaconda.id.storage.encryptedAutoPart or new_luks) and \ + not anaconda.id.storage.encryptionPassphrase: + while True: + (passphrase, retrofit) = anaconda.intf.getLuksPassphrase(preexist=existing_luks) + if passphrase: + anaconda.id.storage.encryptionPassphrase = passphrase + anaconda.id.storage.retrofitPassphrase = retrofit + for dev in anaconda.id.storage.devices: + if dev.format.type == "luks" and not dev.format.exists: + dev.format.passphrase = passphrase + break + else: + rc = anaconda.intf.messageWindow(_("Encrypt device?"), + _("You specified block device encryption " + "should be enabled, but you have not " + "supplied a passphrase. If you do not " + "go back and provide a passphrase, " + "block device encryption will be " + "disabled."), + type="custom", + custom_buttons=[_("Back"), _("Continue")], + default=0) + if rc == 1: + log.info("user elected to not encrypt any devices.") + undoEncryption(anaconda.id.storage) + anaconda.id.storage.encryptedAutoPart = False + break + if anaconda.isKickstart: return @@ -89,6 +124,17 @@ def storageComplete(anaconda): if rc == 0: return DISPATCH_BACK +def undoEncryption(storage): + for device in storage.devicetree.getDevicesByType("luks/dm-crypt"): + if device.exists: + continue + + slave = device.slave + format = device.format + storage.devicetree.registerAction(ActionDestroyFormat(device)) + storage.devicetree.registerAction(ActionDestroyDevice(device)) + storage.devicetree.registerAction(ActionDestroyFormat(slave)) + storage.devicetree.registerAction(ActionCreateFormat(slave, format)) class Storage(object): def __init__(self, anaconda): -- cgit From b58a286c45caf547ef8219af2a60ba8903493355 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 20:14:45 -0600 Subject: Deactivate all devices before processing the actions queue. --- storage/devicetree.py | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/devicetree.py b/storage/devicetree.py index 7db11947b..fcd40b511 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -418,6 +418,7 @@ class DeviceTree(object): for action in self._actions: log.debug("action: %s" % action) self._actions.sort(cmpActions) + self.teardownAll() for action in self._actions: log.info("executing action: %s" % action) if not dryRun: -- cgit From 44825eee5a79305088bef52635b531d38afb69fa Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 21:21:29 -0600 Subject: Make it possible to create encrypted partitions interactively. --- iw/partition_dialog_gui.py | 56 +++++++++++++++++++++++----------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index 04c49e3a7..f4ebdcaaf 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -123,24 +123,6 @@ class PartitionEditor: else: primary = None - format = fmt_class(mountpoint=mountpoint) - if self.lukscb and self.lukscb.get_active(): - luksformat = format - format = getFormat("luks", - passphrase=self.storage.encryptionPassphrase) - luksdev = LUKSDevice("luks%d" % self.storage.nextID, - format=luksformat, - parents=request) - elif self.lukscb and not self.lukscb.get_active() and \ - self.origrequest.format.type == "luks": - # destroy the luks format and the mapped device - luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] - if luksdev: - actions.append(ActionDestroyFormat(luksdev.format)) - actions.append(ActionDestroyDevice(luksdev)) - luksdev = None - actions.append(ActionDestroyFormat(self.origrequest)) - if self.fixedrb.get_active(): grow = None else: @@ -178,20 +160,36 @@ class PartitionEditor: disks.append(disk) # TODO: when will we set bootable? - request = PartitionDevice("new%d" % self.storage.nextID, - format=format, - size=size, - grow=grow, - maxsize=maxsize, - primary=primary, - parents=disks) + request = self.storage.newPartition(size=size, + grow=grow, + maxsize=maxsize, + primary=primary, + parents=disks) + + format = fmt_class(mountpoint=mountpoint) + if self.lukscb and self.lukscb.get_active(): + luksformat = format + format = getFormat("luks", + passphrase=self.storage.encryptionPassphrase) + luksdev = LUKSDevice("luks%d" % self.storage.nextID, + format=luksformat, + parents=request) + elif self.lukscb and not self.lukscb.get_active() and \ + self.origrequest.format.type == "luks": + # destroy the luks format and the mapped device + luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] + if luksdev: + actions.append(ActionDestroyFormat(luksdev.format)) + actions.append(ActionDestroyDevice(luksdev)) + luksdev = None + actions.append(ActionDestroyFormat(self.origrequest)) # we're all set, so create the actions + actions.append(ActionCreateDevice(request)) + actions.append(ActionCreateFormat(request, format)) if luksdev: actions.append(ActionCreateDevice(luksdev)) actions.append(ActionCreateFormat(luksdev)) - actions.append(ActionCreateDevice(request)) - actions.append(ActionCreateFormat(request)) else: # preexisting partition, just set mount point and format flag request = self.origrequest @@ -200,17 +198,19 @@ class PartitionEditor: self.fsoptionsDict["formatcb"].get_active(): fmt_class = self.fsoptionsDict["fstypeCombo"].get_active_value() format = fmt_class(mountpoint=mountpoint) + luksdev = None if self.fsoptionsDict.has_key("lukscb") and \ self.fsoptionsDict["lukscb"].get_active() and \ request.format.type != "luks": luksdev = LUKSDevice("luks%d" % self.storage.nextID, format=format, parents=request) - actions.append(ActionCreateDevice(luksdev)) format = getFormat("luks", device=self.origrequest.path, passphrase=self.storage.encryptionPassphrase) actions.append(ActionCreateFormat(request, format)) + if luksdev: + actions.append(ActionCreateDevice(luksdev)) elif request.format.mountable: request.format.mountpoint = mountpoint -- cgit From 18cc89b25b9627f9623727c4a950aa86dc8d6aea Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 21:22:10 -0600 Subject: Detect newly encrypted devices so we can prompt for passphrase. --- storage/__init__.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/storage/__init__.py b/storage/__init__.py index 11e29d562..5f55790b7 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -80,7 +80,9 @@ def storageComplete(anaconda): for dev in devs: if dev.exists: existing_luks = True - break + else: + new_luks = True + if (anaconda.id.storage.encryptedAutoPart or new_luks) and \ not anaconda.id.storage.encryptionPassphrase: while True: -- cgit From cb427d3cd02def70fd4d9755f0d14d63605eadf6 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 21:23:29 -0600 Subject: Don't mess with action ordering; catch format errors in teardownAll. --- storage/devicetree.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index fcd40b511..a23f97266 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -417,7 +417,7 @@ class DeviceTree(object): self.pruneActions() for action in self._actions: log.debug("action: %s" % action) - self._actions.sort(cmpActions) + #self._actions.sort(cmpActions) self.teardownAll() for action in self._actions: log.info("executing action: %s" % action) @@ -969,7 +969,7 @@ class DeviceTree(object): for device in self.leaves: try: device.teardown(recursive=True) - except DeviceError as e: + except (DeviceError, DeviceFormatError) as e: log.info("teardown of %s failed: %s" % (device.name, e)) def setupAll(self): -- cgit From 4f2e923cb61a7160fc7eb9ce057981d83ff8873d Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 3 Mar 2009 15:43:56 -1000 Subject: Migrate FS user interface plumbing. Hook up the migrate fs UI components with the new storage backend. Highlights: - If user selects Migrate filesystem to, the Format checkbox is disabled. Same for the reverse, selecting Format disables the Migrate options. - Mount point entry box is not disabled for migrate selections. - Wrap the return value in storage.formats.fs.FS.migratable in bool(). If the filter() operation returns a list, that's what we get in the return value rather than True. - Other minor typo and syntax fixes. --- iw/partition_ui_helpers_gui.py | 45 +++++++++++++++++++++++------------------- storage/formats/fs.py | 11 ++++++----- 2 files changed, 31 insertions(+), 25 deletions(-) diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index 723367c9a..4179ab9ec 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -30,7 +30,7 @@ import iutil from constants import * from partIntfHelpers import * from partedUtils import * -from storage.formats import device_formats, getFormat, get_default_filesystem_type +from storage.formats import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -89,17 +89,18 @@ def createMountPointCombo(request, excludeMountPoints=[]): if request.exists and label and label.startswith("/"): mntptlist.append(label) idx = 0 - + for p in defaultMountPoints: - if p in excludeMountPoints: - continue - - if not p in mntptlist and (p[0] == "/"): - mntptlist.append(p) + if p in excludeMountPoints: + continue + + if not p in mntptlist and (p[0] == "/"): + mntptlist.append(p) map(mountCombo.append_text, mntptlist) - if request.format.type and request.format.mountable: + if (request.format.type or request.format.migrate) and \ + request.format.mountable: mountpoint = request.format.mountpoint mountCombo.set_sensitive(1) if mountpoint: @@ -248,9 +249,12 @@ def formatOptionResizeCB(widget, resizesb): if resizesb.get_value_as_int() < lower: resizesb.set_value(adj.lower) -def formatOptionCB(widget, data): - (combowidget, mntptcombo, ofstype, lukscb) = data +def formatMigrateOptionCB(widget, data): + (combowidget, mntptcombo, ofstype, lukscb, othercombo, othercb) = data combowidget.set_sensitive(widget.get_active()) + othercb.set_sensitive(not widget.get_active()) + othercombo.set_sensitive(not widget.get_active()) + if lukscb is not None: lukscb.set_data("formatstate", widget.get_active()) if not widget.get_active(): @@ -262,11 +266,11 @@ def formatOptionCB(widget, data): # inject event for fstype menu if widget.get_active(): - fstype = combowidget.get_active_value() - setMntPtComboStateFromType(fstype, mntptcombo) + fstype = combowidget.get_active_value() + setMntPtComboStateFromType(fstype, mntptcombo) combowidget.grab_focus() else: - setMntPtComboStateFromType(ofstype, mntptcombo) + setMntPtComboStateFromType(ofstype, mntptcombo) def noformatCB(widget, data): (combowidget, mntptcombo, ofstype) = data @@ -274,7 +278,7 @@ def noformatCB(widget, data): # inject event for fstype menu if widget.get_active(): - setMntPtComboStateFromType(ofstype, mntptcombo) + setMntPtComboStateFromType(ofstype, mntptcombo) """ createPreExistFSOptionSection: given inputs for a preexisting partition, @@ -314,10 +318,6 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, # this gets added to the table a bit later on lukscb = gtk.CheckButton(_("_Encrypt")) - formatcb.connect("toggled", formatOptionCB, - (fstypeCombo, mountCombo, ofstype, lukscb)) - - if origrequest.format.migratable: migratecb = gtk.CheckButton(label=_("Mi_grate filesystem to:")) migratecb.set_active(istruefalse(origrequest.format.migrate)) @@ -333,12 +333,17 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, row = row + 1 rc["migratecb"] = migratecb rc["migfstypeCombo"] = migfstypeCombo - migratecb.connect("toggled", formatOptionCB, - (migfstypeCombo, mountCombo, ofstype, None)) + migratecb.connect("toggled", formatMigrateOptionCB, + (migfstypeCombo, mountCombo, ofstype, None, + fstypeCombo, formatcb)) else: migratecb = None migfstypeCombo = None + formatcb.connect("toggled", formatMigrateOptionCB, + (fstypeCombo, mountCombo, ofstype, lukscb, + migfstypeCombo, migratecb)) + if origrequest.resizable: resizecb = gtk.CheckButton(label=_("_Resize")) resizecb.set_active(origrequest.targetSize != origrequest.currentSize) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 694b1f406..7c73ff4e8 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -599,11 +599,12 @@ class FS(DeviceFormat): @property def migratable(self): """ Can filesystems of this type be migrated? """ - return (self._migratable and self.migratefsProg and - filter(lambda d: os.access("%s/%s" % (d, self.migratefsProg), - os.X_OK), - os.environ["PATH"].split(":")) and - self.migrationTarget) + return bool(self._migratable and self.migratefsProg and + filter(lambda d: os.access("%s/%s" + % (d, self.migratefsProg,), + os.X_OK), + os.environ["PATH"].split(":")) and + self.migrationTarget) def _setMigrate(self, migrate): if not migrate: -- cgit From bd891993928fa57f88846941e957b799031b292d Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 3 Mar 2009 16:37:15 -1000 Subject: self.format -> self.device.format The format object is self.device.format in DeviceAction. --- storage/deviceaction.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/deviceaction.py b/storage/deviceaction.py index 462fc0937..a30314955 100644 --- a/storage/deviceaction.py +++ b/storage/deviceaction.py @@ -178,8 +178,8 @@ class DeviceAction(object): if self.isResize(): s += " (%s)" % resize_strings[self.dir] if self.isFormat(): - if self.format: - fmt_type = self.format.type + if self.device.format: + fmt_type = self.device.format.type else: fmt_type = None s += " %s on" % fmt_type -- cgit From e1a3a536de4df277a3a201652ebb242c8c0d127f Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 3 Mar 2009 16:50:18 -1000 Subject: Handle instances where migrate is not possible. If we're dealing with a non-migratable filesystem, othercb and othercombo will be None here. --- iw/partition_ui_helpers_gui.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index 4179ab9ec..87d816fe8 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -252,8 +252,12 @@ def formatOptionResizeCB(widget, resizesb): def formatMigrateOptionCB(widget, data): (combowidget, mntptcombo, ofstype, lukscb, othercombo, othercb) = data combowidget.set_sensitive(widget.get_active()) - othercb.set_sensitive(not widget.get_active()) - othercombo.set_sensitive(not widget.get_active()) + + if othercb is not None: + othercb.set_sensitive(not widget.get_active()) + + if othercombo is not None: + othercombo.set_sensitive(not widget.get_active()) if lukscb is not None: lukscb.set_data("formatstate", widget.get_active()) -- cgit From 8fe8f5478e84adbe28cca63d72b9ea6552ae79e9 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 3 Mar 2009 16:58:28 -1000 Subject: Return whatever we get here, we don't care if bootDev is None here. This function will return None until the user sets up a /boot or a / filesystem. --- platform.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/platform.py b/platform.py index ef9ff0c8d..4adc5d106 100644 --- a/platform.py +++ b/platform.py @@ -62,12 +62,7 @@ class Platform(object): raise NotImplementedError("bootDevice not implemented for this platform") mntDict = self._mntDict() - bootDev = mntDict.get("/boot", mntDict.get("/")) - - if not bootDev: - raise DeviceError("No bootable device found") - else: - return bootDev + return mntDict.get("/boot", mntDict.get("/")) @property def bootFSType(self): -- cgit From 65f40c6a81f5241f715d410a3d5012fa9b57b78c Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 3 Mar 2009 18:15:17 -1000 Subject: More UI plumbing for filesystem migration support. Fixes for the edit partition dialog. --- iw/partition_ui_helpers_gui.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index 87d816fe8..b8cae4a6b 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -250,11 +250,16 @@ def formatOptionResizeCB(widget, resizesb): resizesb.set_value(adj.lower) def formatMigrateOptionCB(widget, data): + (sensitive,) = widget.get_properties('sensitive') + if not sensitive: + return + (combowidget, mntptcombo, ofstype, lukscb, othercombo, othercb) = data combowidget.set_sensitive(widget.get_active()) if othercb is not None: othercb.set_sensitive(not widget.get_active()) + othercb.set_active(False) if othercombo is not None: othercombo.set_sensitive(not widget.get_active()) @@ -274,6 +279,9 @@ def formatMigrateOptionCB(widget, data): setMntPtComboStateFromType(fstype, mntptcombo) combowidget.grab_focus() else: + if isinstance(ofstype, type(ofstype)): + ofstype = type(ofstype) + setMntPtComboStateFromType(ofstype, mntptcombo) def noformatCB(widget, data): -- cgit From 32bd0d58fadc536892826185b5f85f6107b0da86 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 21:34:03 -0600 Subject: Fix typo in doDeleteDevice. --- partIntfHelpers.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/partIntfHelpers.py b/partIntfHelpers.py index 080c0cfa5..ed9dfe5c0 100644 --- a/partIntfHelpers.py +++ b/partIntfHelpers.py @@ -127,7 +127,7 @@ def doDeleteDevice(intf, storage, device, confirm=1, quiet=0): if reason: intf.messageWindow(_("Unable To Delete"), reason, - customer_icon="error") + custom_icon="error") return False if confirm and not confirmDelete(intf, device): -- cgit From 040d450992db157eb7e83c8fc8cbc70e27597da9 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 3 Mar 2009 23:20:33 -0600 Subject: Fix match on "name" attribute we can lookup by, eg: "software RAID". --- storage/formats/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py index 860292362..2b6acae00 100644 --- a/storage/formats/__init__.py +++ b/storage/formats/__init__.py @@ -118,7 +118,7 @@ def get_device_format_class(fmt_type): fmt = device_formats.get(fmt_type) if not fmt: for fmt_class in device_formats.values(): - if fmt_type == fmt_class.name: + if fmt_type and fmt_type == fmt_class._name: fmt = fmt_class break elif fmt_type in fmt_class._udevTypes: -- cgit From 0dd718a0112fbe7798fadb1231c14c15e3280021 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 00:37:35 -0600 Subject: Several fixes to raid dialog. The biggest change is creating a new MDRaidArrayDevice instance whenever the user hits "ok" and the device is not preexisting. --- iw/raid_dialog_gui.py | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index df9501d68..c9d99a24c 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -28,6 +28,7 @@ import gtk import datacombo import gui +from storage.devicelibs import mdraid from storage.devices import * from storage.deviceaction import * from partition_ui_helpers_gui import * @@ -177,7 +178,7 @@ class RaidEditor: if self.fsoptionsDict.has_key("lukscb") and \ self.fsoptionsDict["lukscb"].get_active() and \ self.origrequest.format.type != "luks": - luksdev = LUKSDevice("luks-%s" % request.name, + luksdev = LUKSDevice("luks-%s" % self.origrequest.name, format=format, parents=self.origrequest) format = getFormat("luks", @@ -191,7 +192,7 @@ class RaidEditor: actions.append(ActionDestroyFormat(luksdev.format)) actions.append(ActionDestroyDevice(luksdev)) luksdev = None - actions.append(ActionDestroyFormat(request)) + actions.append(ActionDestroyFormat(self.origrequest)) else: # existing device @@ -202,25 +203,25 @@ class RaidEditor: format = fmt_class(mountpoint=mountpoint) if self.fsoptionsDict.has_key("lukscb") and \ self.fsoptionsDict["lukscb"].get_active() and \ - request.format.type != "luks": - luksdev = LUKSDevice("luks-%s" % request.name, + self.origrequest.format.type != "luks": + luksdev = LUKSDevice("luks-%s" % self.origrequest.name, format=format, - parents=request) + parents=self.origrequest) format = getFormat("luks", device=self.origrequest.path, passphrase=self.storage.encryptionPassphrase) elif self.fsoptionsDict.has_key("lukscb") and \ not self.fsoptionsDict["lukscb"].get_active() and \ - request.format.type == "luks": + self.origrequest.format.type == "luks": # destroy luks format and mapped device luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] if luksdev: actions.append(ActionDestroyFormat(luksdev.format)) actions.append(ActionDestroyDevice(luksdev)) luksdev = None - actions.append(ActionDestroyFormat(request)) - elif request.format.mountable: - request.format.mountpoint = mountpoint + actions.append(ActionDestroyFormat(self.origrequest)) + elif self.origrequest.format.mountable: + self.origrequest.format.mountpoint = mountpoint if self.fsoptionsDict.has_key("migratecb") and \ self.fsoptionsDict["migratecb"].get_active(): @@ -230,17 +231,28 @@ class RaidEditor: usedev = origrequest migrate = True - if request.format.exists and \ - self.storage.formatByDefault(request): + if self.origrequest.format.exists and \ + self.storage.formatByDefault(self.origrequest): if not queryNoFormatPreExisting(self.intf): continue # everything ok, break out break - # FIXME: only create a new create action if the device isn't in the tree - actions.append(ActionCreateDevice(self.origrequest)) - actions.append(ActionCreateFormat(self.origrequest)) + if not self.origrequest.exists: + members = len(raidmembers) - spares + level = int(raidlevel.lower().replace("raid", "")) + request = self.storage.newMDArray(minor=raidminor, + level=level, + format=format, + parents=raidmembers, + memberDevices=members) + actions.append(ActionCreateDevice(request)) + actions.append(ActionCreateFormat(request)) + elif format: + actions.append(ActionCreateFormat(self.origreqest, format)) + + if luksdev: actions.append(ActionCreateDevice(luksdev)) actions.append(ActionCreateFormat(luksdev)) @@ -360,7 +372,7 @@ class RaidEditor: maintable.attach(lbl, 0, 1, row, row + 1) if not origrequest.exists: - availminors = self.storage.unusedMDMinors()[:16] + availminors = self.storage.unusedMDMinors[:16] reqminor = origrequest.minor if reqminor is not None: availminors.append(reqminor) @@ -406,7 +418,7 @@ class RaidEditor: if not origrequest.exists: - self.levelcombo = self.createRaidLevelMenu(raid_levels, + self.levelcombo = self.createRaidLevelMenu(mdraid.raid_levels, origrequest.level) lbl.set_mnemonic_widget(self.levelcombo) else: -- cgit From 7f32a5e8ec5e4cc3737a52e8fcfbb696bfe830bd Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 00:40:12 -0600 Subject: Fixes to Storage.newMDArray to ensure minor is an int. --- storage/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 5f55790b7..cba5d0990 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -481,14 +481,14 @@ class Storage(object): None)) if kwargs.has_key("minor"): - minor = str(kwargs.pop("minor")) + kwargs["minor"] = int(kwargs["minor"]) else: - kwargs["minor"] = str(self.unusedMDMinors[0]) + kwargs["minor"] = self.unusedMDMinors[0] if kwargs.has_key("name"): name = kwargs.pop("name") else: - name = "md%s" % kwargs["minor"] + name = "md%d" % kwargs["minor"] return MDRaidArrayDevice(name, *args, **kwargs) -- cgit From e88ee0fc03636968a3a5438b26d11db0f71907c1 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 00:41:01 -0600 Subject: Use properties for totalDevices, memberDevices, spares. This does verification you don't get using simple attributes. --- storage/devices.py | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 030d2ec07..41bf0a1d7 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -1773,8 +1773,8 @@ class MDRaidArrayDevice(StorageDevice): parents=parents, sysfsPath=sysfsPath) self.level = level self.uuid = uuid - self.memberDevices = numeric_type(memberDevices) - self.totalDevices = numeric_type(totalDevices) + self._totalDevices = numeric_type(totalDevices) + self._memberDevices = numeric_type(memberDevices) self.sysfsPath = "/devices/virtual/block/%s" % name """ FIXME: Bitmap is more complicated than this. @@ -1798,6 +1798,28 @@ class MDRaidArrayDevice(StorageDevice): fmt = "ARRAY level=%s num-devices=%d UUID=%s" return fmt % (self.level, self.memberDevices, self.uuid) + @property + def totalDevices(self): + """ Total number of devices in the array, including spares. """ + count = len(self.parents) + if self.exists: + count = self._totalDevices + return count + + def _getMemberDevices(self): + return self._memberDevices + + def _setMemberDevices(self, number): + if not isinstance(number, int): + raise ValueError("memberDevices is an integer") + + if number > self.totalDevices: + raise ValueError("memberDevices cannot be greater than totalDevices") + self._memberDevices = number + + memberDevices = property(_getMemberDevices, _setMemberDevices, + doc="number of member devices") + def _getSpares(self): spares = 0 if self.memberDevices is not None: -- cgit From db23eb26759f32fcda3868e7913c5d6cca900040 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 00:42:48 -0600 Subject: Add special handling in getDependentDevices for extended partitions. It just seems like more work than it is worth to have logical partitions' parents be the extended instead of the disk. --- storage/devicetree.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/storage/devicetree.py b/storage/devicetree.py index a23f97266..7f511347c 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -552,9 +552,24 @@ class DeviceTree(object): The list includes both direct and indirect dependents. """ dependents = [] + + # special handling for extended partitions since the logical + # partitions and their deps effectively depend on the extended + logicals = [] + if dep.type == "partition" and dep.isExtended: + # collect all of the logicals on the same disk + for part in self.getDevicesByType("partition"): + if part.isLogical and part.disk == dep.disk: + logicals.append(part) + for device in self.devices.values(): if device.dependsOn(dep): dependents.append(device) + else: + for logical in logicals: + if device.dependsOn(logical): + dependents.append(device) + break return dependents -- cgit From 11593d3a278fe27884afe35c5473da2e5a0b4078 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 00:44:45 -0600 Subject: Don't traceback if options is None in doFormat. --- storage/formats/fs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 7c73ff4e8..ba0b111d3 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -197,7 +197,8 @@ class FS(DeviceFormat): def _getFormatArgs(self, options=None): argv = [] - argv.extend(options) + if options and isinstance(options, list): + argv.extend(options) argv.extend(self.defaultFormatOptions) argv.append(self.device) return argv @@ -777,7 +778,8 @@ class BTRFS(FS): def _getFormatArgs(self, options=None): argv = [] - argv.extend(options) + if options and isinstance(options, list): + argv.extend(options) argv.extend(self.defaultFormatArgs) if self.fslabel: argv.extend(["-L", self.fslabel]) -- cgit From e9c2494233d0fa3d65827d32710b4774bda5a987 Mon Sep 17 00:00:00 2001 From: Martin Gracik Date: Sat, 28 Feb 2009 21:17:01 +0100 Subject: Added unittests for devicelibs lvm.py and swap.py; Corrected errors in lvm.py and swap.py --- storage/devicelibs/lvm.py | 33 +++-- storage/devicelibs/swap.py | 4 +- tests/storage/devicelibs/baseclass.py | 43 +++++++ tests/storage/devicelibs/lvm.py | 229 ++++++++++++++++++++++++++++++++++ tests/storage/devicelibs/swap.py | 63 ++++++++++ 5 files changed, 357 insertions(+), 15 deletions(-) create mode 100644 tests/storage/devicelibs/baseclass.py create mode 100644 tests/storage/devicelibs/lvm.py create mode 100644 tests/storage/devicelibs/swap.py diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py index cd3970745..bd39fc9b8 100644 --- a/storage/devicelibs/lvm.py +++ b/storage/devicelibs/lvm.py @@ -159,18 +159,25 @@ def pvinfo(device): if not vals: raise LVMError("pvinfo failed for %s" % device) - info = {'pv_name': vals[0], - 'vg_name': vals[2], - 'vg_uuid': vals[3]} + # don't raise an exception if pv is not a part of any vg + pv_name = vals[0] + try: + vg_name, vg_uuid = vals[2], vals[3] + except IndexError: + vg_name, vg_uuid = "", "" + + info = {'pv_name': pv_name, + 'vg_name': vg_name, + 'vg_uuid': vg_uuid} + return info -def vgcreate(vg_name, pvs, pe_size): +def vgcreate(vg_name, pv_list, pe_size): argv = ["vgcreate"] if pe_size: argv.extend(["-s", "%dM" % pe_size]) - pv_list = " ".join(pvs) argv.append(vg_name) - argv.append(pv_list) + argv.extend(pv_list) rc = iutil.execWithRedirect("lvm", argv, stdout = "/dev/tty5", @@ -190,7 +197,7 @@ def vgremove(vg_name): raise LVMError("vgremove failed for %s" % vg_name) def vgactivate(vg_name): - rc = iutil.execWithRedirect("lvm", ["vgchange" "-a", "y", vg_name], + rc = iutil.execWithRedirect("lvm", ["vgchange", "-a", "y", vg_name], stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -207,8 +214,7 @@ def vgdeactivate(vg_name): raise LVMError("vgdeactivate failed for %s" % vg_name) def vgreduce(vg_name, pv_list): - pvs = " ".join(pv_list) - rc = iutil.execWithRedirect("lvm", ["vgreduce", vg_name, pvs], + rc = iutil.execWithRedirect("lvm", ["vgreduce", vg_name] + pv_list, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -235,10 +241,9 @@ def lvs(vg_name): buf = iutil.execWithCapture("lvm", ["lvs", "--noheadings", "--nosuffix", "--units", "m", "-o", - "lv_name,lv_uuid,lv_size"], + "lv_name,lv_uuid,lv_size", vg_name], stderr="/dev/tty5") - lvs = {} for line in buf.splitlines(): line = line.strip() @@ -247,6 +252,10 @@ def lvs(vg_name): (name, uuid, size) = line.split() lvs[name] = {"size": size, "uuid": uuid} + + if not lvs: + raise LVMError(_("lvs failed for %s" % vg_name)) + return lvs def lvcreate(vg_name, lv_name, size): @@ -307,5 +316,3 @@ def lvdeactivate(vg_name, lv_name): if rc: raise LVMError("lvdeactivate failed for %s" % lv_path) - - diff --git a/storage/devicelibs/swap.py b/storage/devicelibs/swap.py index 8ee5b5bb3..38000eb6f 100644 --- a/storage/devicelibs/swap.py +++ b/storage/devicelibs/swap.py @@ -23,6 +23,7 @@ import resource import iutil +import resource from ..errors import * @@ -71,7 +72,7 @@ def swapon(device, priority=None): argv = [] if isinstance(priority, int) and 0 <= priority <= 32767: - argv.extend(["-p", priority]) + argv.extend(["-p", "%d" % priority]) argv.append(device) rc = iutil.execWithRedirect("swapon", @@ -105,4 +106,3 @@ def swapstatus(device): return status - diff --git a/tests/storage/devicelibs/baseclass.py b/tests/storage/devicelibs/baseclass.py new file mode 100644 index 000000000..d1264f5b6 --- /dev/null +++ b/tests/storage/devicelibs/baseclass.py @@ -0,0 +1,43 @@ +import unittest +import os +import subprocess + +class TestDevicelibs(unittest.TestCase): + + _LOOP_DEVICES = (("/dev/loop0", "/tmp/test-virtdev0"), + ("/dev/loop1", "/tmp/test-virtdev1")) + + ((_LOOP_DEV0, _LOOP_FILE0), (_LOOP_DEV1, _LOOP_FILE1)) = _LOOP_DEVICES + + def setUp(self): + for dev, file in self._LOOP_DEVICES: + proc = subprocess.Popen(["dd", "if=/dev/zero", "of=%s" % file, "bs=1024", "count=102400"]) + while True: + proc.communicate() + if proc.returncode is not None: + rc = proc.returncode + break + if rc: + raise OSError, "dd failed creating the file %s" % file + + proc = subprocess.Popen(["losetup", dev, file]) + while True: + proc.communicate() + if proc.returncode is not None: + rc = proc.returncode + break + if rc: + raise OSError, "losetup failed setting up the loop device %s" % dev + + def tearDown(self): + for dev, file in self._LOOP_DEVICES: + proc = subprocess.Popen(["losetup", "-d", dev]) + while True: + proc.communicate() + if proc.returncode is not None: + rc = proc.returncode + break + if rc: + raise OSError, "losetup failed removing the loop device %s" % dev + + os.remove(file) diff --git a/tests/storage/devicelibs/lvm.py b/tests/storage/devicelibs/lvm.py new file mode 100644 index 000000000..210ad9d95 --- /dev/null +++ b/tests/storage/devicelibs/lvm.py @@ -0,0 +1,229 @@ +import baseclass +import unittest +import storage.devicelibs.lvm as lvm + +class TestLVM(baseclass.TestDevicelibs): + + def testLVMStuff(self): + ## + ## pvcreate + ## + # pass + for dev, file in self._LOOP_DEVICES: + self.assertEqual(lvm.pvcreate(dev), None) + + # fail + self.assertRaises(lvm.LVMError, lvm.pvcreate, "/not/existing/device") + + ## + ## pvresize + ## + # pass + for dev, file in self._LOOP_DEVICES: + self.assertEqual(lvm.pvresize(dev, 50), None) + self.assertEqual(lvm.pvresize(dev, 100), None) + + # fail + self.assertRaises(lvm.LVMError, lvm.pvresize, "/not/existing/device", 50) + + ## + ## vgcreate + ## + # pass + self.assertEqual(lvm.vgcreate("test-vg", [self._LOOP_DEV0, self._LOOP_DEV1], 4), None) + + # fail + self.assertRaises(lvm.LVMError, lvm.vgcreate, "another-vg", ["/not/existing/device"], 4) + # vg already exists + self.assertRaises(lvm.LVMError, lvm.vgcreate, "test-vg", [self._LOOP_DEV0], 4) + # pe size must be power of 2 + self.assertRaises(lvm.LVMError, lvm.vgcreate, "another-vg", [self._LOOP_DEV0], 5) + + ## + ## pvremove + ## + # fail + # cannot remove pv now with vg created + self.assertRaises(lvm.LVMError, lvm.pvremove, self._LOOP_DEV0) + + ## + ## vgdeactivate + ## + # pass + self.assertEqual(lvm.vgdeactivate("test-vg"), None) + + # fail + self.assertRaises(lvm.LVMError, lvm.vgdeactivate, "wrong-vg-name") + + ## + ## vgreduce + ## + # pass + self.assertEqual(lvm.vgreduce("test-vg", [self._LOOP_DEV1]), None) + + # fail + self.assertRaises(lvm.LVMError, lvm.vgreduce, "wrong-vg-name", [self._LOOP_DEV1]) + self.assertRaises(lvm.LVMError, lvm.vgreduce, "test-vg", ["/not/existing/device"]) + + ## + ## vgactivate + ## + # pass + self.assertEqual(lvm.vgactivate("test-vg"), None) + + # fail + self.assertRaises(lvm.LVMError, lvm.vgactivate, "wrong-vg-name") + + ## + ## pvinfo + ## + # pass + self.assertEqual(lvm.pvinfo(self._LOOP_DEV0)["pv_name"], self._LOOP_DEV0) + # no vg + self.assertEqual(lvm.pvinfo(self._LOOP_DEV1)["pv_name"], self._LOOP_DEV1) + + # fail + self.assertRaises(lvm.LVMError, lvm.pvinfo, "/not/existing/device") + + ## + ## vginfo + ## + # pass + self.assertEqual(lvm.vginfo("test-vg")["pe_size"], "4.00") + + # fail + self.assertRaises(lvm.LVMError, lvm.vginfo, "wrong-vg-name") + + ## + ## lvcreate + ## + # pass + self.assertEqual(lvm.lvcreate("test-vg", "test-lv", 10), None) + + # fail + self.assertRaises(lvm.LVMError, lvm.lvcreate, "wrong-vg-name", "another-lv", 10) + + ## + ## lvdeactivate + ## + # pass + self.assertEqual(lvm.lvdeactivate("test-vg", "test-lv"), None) + + # fail + self.assertRaises(lvm.LVMError, lvm.lvdeactivate, "test-vg", "wrong-lv-name") + self.assertRaises(lvm.LVMError, lvm.lvdeactivate, "wrong-vg-name", "test-lv") + self.assertRaises(lvm.LVMError, lvm.lvdeactivate, "wrong-vg-name", "wrong-lv-name") + + ## + ## lvresize + ## + # pass + self.assertEqual(lvm.lvresize("test-vg", "test-lv", 60), None) + + # fail + self.assertRaises(lvm.LVMError, lvm.lvresize, "test-vg", "wrong-lv-name", 80) + self.assertRaises(lvm.LVMError, lvm.lvresize, "wrong-vg-name", "test-lv", 80) + self.assertRaises(lvm.LVMError, lvm.lvresize, "wrong-vg-name", "wrong-lv-name", 80) + # changing to same size + self.assertRaises(lvm.LVMError, lvm.lvresize, "test-vg", "test-lv", 60) + + ## + ## lvactivate + ## + # pass + self.assertEqual(lvm.lvactivate("test-vg", "test-lv"), None) + + # fail + self.assertRaises(lvm.LVMError, lvm.lvactivate, "test-vg", "wrong-lv-name") + self.assertRaises(lvm.LVMError, lvm.lvactivate, "wrong-vg-name", "test-lv") + self.assertRaises(lvm.LVMError, lvm.lvactivate, "wrong-vg-name", "wrong-lv-name") + + ## + ## lvs + ## + # pass + self.assertEqual(lvm.lvs("test-vg")["test-lv"]["size"], "60.00") + + # fail + self.assertRaises(lvm.LVMError, lvm.lvs, "wrong-vg-name") + + ## + ## has_lvm + ## + # pass + self.assertEqual(lvm.has_lvm(), True) + + # fail + #TODO + + ## + ## lvremove + ## + # pass + self.assertEqual(lvm.lvdeactivate("test-vg", "test-lv"), None) # is deactivation needed? + self.assertEqual(lvm.lvremove("test-vg", "test-lv"), None) + + # fail + self.assertRaises(lvm.LVMError, lvm.lvremove, "test-vg", "wrong-lv-name") + self.assertRaises(lvm.LVMError, lvm.lvremove, "wrong-vg-name", "test-lv") + self.assertRaises(lvm.LVMError, lvm.lvremove, "wrong-vg-name", "wrong-lv-name") + # lv already removed + self.assertRaises(lvm.LVMError, lvm.lvremove, "test-vg", "test-lv") + + ## + ## vgremove + ## + # pass + self.assertEqual(lvm.vgremove("test-vg"), None) + + # fail + self.assertRaises(lvm.LVMError, lvm.vgremove, "wrong-vg-name") + # vg already removed + self.assertRaises(lvm.LVMError, lvm.vgremove, "test-vg") + + ## + ## pvremove + ## + # pass + for dev, file in self._LOOP_DEVICES: + self.assertEqual(lvm.pvremove(dev), None) + + # fail + self.assertRaises(lvm.LVMError, lvm.pvremove, "/not/existing/device") + # pv already removed + self.assertRaises(lvm.LVMError, lvm.pvremove, self._LOOP_DEV0) + + #def testGetPossiblePhysicalExtents(self): + # pass + self.assertEqual(lvm.getPossiblePhysicalExtents(4), + filter(lambda pe: pe > 4, map(lambda power: 2**power, xrange(3, 25)))) + self.assertEqual(lvm.getPossiblePhysicalExtents(100000), + filter(lambda pe: pe > 100000, map(lambda power: 2**power, xrange(3, 25)))) + + #def testGetMaxLVSize(self): + # pass + # why do we specify the PE ? not needed... + self.assertEqual(lvm.getMaxLVSize(4), 16*1024**2) + + #def testSafeLVMName(self): + # pass + self.assertEqual(lvm.safeLvmName("/strange/lv*name5"), "strange_lvname5") + + #def testClampSize(self): + # pass + self.assertEqual(lvm.clampSize(10, 4), 8L) + self.assertEqual(lvm.clampSize(10, 4, True), 12L) + + #def testVGUsedSpace(self): + #TODO + pass + + #def testVGFreeSpace(self): + #TODO + pass + + +if __name__ == "__main__": + suite = unittest.TestLoader().loadTestsFromTestCase(TestLVM) + unittest.TextTestRunner(verbosity=2).run(suite) + diff --git a/tests/storage/devicelibs/swap.py b/tests/storage/devicelibs/swap.py new file mode 100644 index 000000000..b1e9bd3b5 --- /dev/null +++ b/tests/storage/devicelibs/swap.py @@ -0,0 +1,63 @@ +import baseclass +import unittest +import storage.devicelibs.swap as swap + +class TestSwap(baseclass.TestDevicelibs): + + def runTest(self): + ## + ## mkswap + ## + # pass + self.assertEqual(swap.mkswap(self._LOOP_DEV0, "swap"), None) + + # fail + self.assertRaises(swap.SwapError, swap.mkswap, "/not/existing/device") + + ## + ## swapon + ## + # pass + self.assertEqual(swap.swapon(self._LOOP_DEV0, 1), None) + + # fail + self.assertRaises(swap.SwapError, swap.swapon, "/not/existing/device") + # not a swap partition + self.assertRaises(swap.SwapError, swap.swapon, self._LOOP_DEV1) + + # pass + # make another swap + self.assertEqual(swap.mkswap(self._LOOP_DEV1, "another-swap"), None) + self.assertEqual(swap.swapon(self._LOOP_DEV1), None) + + ## + ## swapstatus + ## + # pass + self.assertEqual(swap.swapstatus(self._LOOP_DEV0), True) + self.assertEqual(swap.swapstatus(self._LOOP_DEV1), True) + + # does not fail + self.assertEqual(swap.swapstatus("/not/existing/device"), False) + + ## + ## swapoff + ## + # pass + self.assertEqual(swap.swapoff(self._LOOP_DEV1), None) + + # check status + self.assertEqual(swap.swapstatus(self._LOOP_DEV0), True) + self.assertEqual(swap.swapstatus(self._LOOP_DEV1), False) + + self.assertEqual(swap.swapoff(self._LOOP_DEV0), None) + + # fail + self.assertRaises(swap.SwapError, swap.swapoff, "/not/existing/device") + # already off + self.assertRaises(swap.SwapError, swap.swapoff, self._LOOP_DEV0) + + +if __name__ == "__main__": + suite = unittest.TestLoader().loadTestsFromTestCase(TestSwap) + unittest.TextTestRunner(verbosity=2).run(suite) -- cgit From 9fc3c405e8e7732d700a8c7ad2e794933d6d16ab Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 10:59:33 -0600 Subject: Conditionalize ext3->ext4 migrate on "ext4migrate" cmdline arg. --- storage/formats/fs.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index ba0b111d3..68bc681c9 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -722,6 +722,12 @@ class Ext3FS(Ext2FS): _migrationTarget = "ext4" _defaultMigrateOptions = ["-O", "extents"] + @property + def migratable(self): + """ Can filesystems of this type be migrated? """ + return (flags.cmdline.has_key("ext4migrate") and + Ext2FS.migratable(self)) + register_device_format(Ext3FS) -- cgit From 6ac5cf02d862495e0ffc324825268998b7a44aca Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 14:26:14 -0600 Subject: Make the lvm dialogs minimally functional. --- iw/lvm_dialog_gui.py | 100 +++++++++++++++++---------------------------------- 1 file changed, 32 insertions(+), 68 deletions(-) diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index a85fb7178..b3e210101 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -41,25 +41,12 @@ log = logging.getLogger("anaconda") class VolumeGroupEditor: def numAvailableLVSlots(self): - return max(0, lvm.MAX_LV_SLOTS - len(self.logvolreqs)) - - def computeSpaceValues(self, alt_pvlist=None, usepe=None): - # FIXME: stop using this in favor of the vg's methods - if usepe is None: - pesize = long(self.peCombo.get_active_value()) / 1024.0 - else: - pesize = long(usepe) - - if alt_pvlist is None: - pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model()) - else: - pvlist = alt_pvlist - - # XXX this should all be computed using self.vg - vgsize = self.computeVGSize(pvlist, pesize) - vgused = self.computeLVSpaceNeeded(self.vg.lvs) - vgfree = vgsize - vgused + return max(0, lvm.MAX_LV_SLOTS - len(self.vg.lvs)) + def computeSpaceValues(self): + vgsize = self.vg.size + vgfree = self.vg.freeSpace + vgused = vgsize - vgfree return (vgsize, vgused, vgfree) def getPVWastedRatio(self, newpe): @@ -84,7 +71,9 @@ class VolumeGroupEditor: try: pesize = int(self.peCombo.get_active_value()) except: - pesize = 32768 + pesize = self.vg.peSize + + # FIXME: move this logic into a property of LVMVolumeGroupDevice pvsize = lvm.clampPVSize(pv.size, pesize) - int(pesize/1024) if first: minpvsize = pvsize @@ -144,7 +133,7 @@ class VolumeGroupEditor: # XXX this is very sneaky, just changing the lvs' size attributes. # will we suffer for it? almost certainly. - for lv in self.logvolreqs: + for lv in self.vg.lvs: osize = lv.size nsize = lvm.clampSize(osize, newpe, roundup=1) lv.size = nsize @@ -216,7 +205,7 @@ class VolumeGroupEditor: else: self.updateLogVolStore() else: - maxlv = lvm.getMaxLVSize(curpe) + maxlv = lvm.getMaxLVSize() for lv in self.vg.lvs: if lv.size > maxlv: self.intf.messageWindow(_("Not enough space"), @@ -235,7 +224,7 @@ class VolumeGroupEditor: widget.set_data("lastpe", curval) widget.set_data("lastidx", widget.get_active()) - # XXX this is pretty sneaky, too + # now actually set the VG's extent size self.vg.peSize = curpe self.updateAllowedLvmPartitionsList(self.availlvmparts, self.lvmlist) @@ -294,24 +283,21 @@ class VolumeGroupEditor: else: pvlist.remove(pv) - - (availSpaceMB, neededSpaceMB, fspace) = self.computeSpaceValues(alt_pvlist=pvlist) - if availSpaceMB < neededSpaceMB: - self.intf.messageWindow(_("Not enough space"), - _("You cannot remove this physical " - "volume because otherwise the " - "volume group will be too small to " - "hold the currently defined logical " - "volumes."), custom_icon="error") - return False - - # XXX this is pretty sneaky if val: self.vg._addPV(pv) else: - self.vg._removePV(pv) + try: + self.vg._removePV(pv) + except DeviceError as e: + self.intf.messageWindow(_("Not enough space"), + _("You cannot remove this physical " + "volume because otherwise the " + "volume group will be too small to " + "hold the currently defined logical " + "volumes."), custom_icon="error") + return False - self.updateVGSpaceLabels(alt_pvlist = pvlist) + self.updateVGSpaceLabels() return True @@ -353,6 +339,8 @@ class VolumeGroupEditor: if include: partlist.append_row((device.name, size_string), selected) + if selected: + self.vg._addPV(device) return (partlist, sw) @@ -477,14 +465,7 @@ class VolumeGroupEditor: row += 1 if not lv.exists: - pesize = int(self.peCombo.get_active_value()) / 1024.0 - (total, used, free) = self.computeSpaceValues(usepe=pesize) - maxlv = min(lvm.getMaxLVSize(pesize), free) - - # add in size of current logical volume if it has a size - if lv and not isNew: - maxlv += lv.size - + maxlv = min(lvm.getMaxLVSize(), lv.size) maxlabel = createAlignedLabel(_("(Max size is %s MB)") % (maxlv,)) maintable.attach(maxlabel, 1, 2, row, row + 1) @@ -569,7 +550,7 @@ class VolumeGroupEditor: used = 0 curmntpt = format.mountpoint - for _lv in self.logvolreqs: + for _lv in self.vg.lvs: if _lv.format.type == "luks": # XXX this won't work if the lvs haven't been # added to the tree yet @@ -615,7 +596,7 @@ class VolumeGroupEditor: # check that size specification is within limits pesize = int(self.peCombo.get_active_value()) / 1024.0 size = lvm.clampSize(size, pesize, roundup=True) - maxlv = lvm.getMaxLVSize(pesize) + maxlv = lvm.getMaxLVSize() if size > maxlv: self.intf.messageWindow(_("Not enough space"), _("The current requested size " @@ -751,7 +732,7 @@ class VolumeGroupEditor: "logical volumes"), custom_icon="error") return - lv = self.storage.newLV(self.vg, + lv = self.storage.newLV(vg=self.vg, fmt_type=self.storage.defaultFSType, size=free) self.editLogicalVolume(lv, isNew = 1) @@ -850,7 +831,7 @@ class VolumeGroupEditor: mntpt = getattr(usedev.format, "mountpoint", "") if lv.lvname: - self.logvolstore.set_value(iter, 0, lvname) + self.logvolstore.set_value(iter, 0, lv.lvname) if usedev.format.type and usedev.format.mountable: self.logvolstore.set_value(iter, 1, mntpt) @@ -859,13 +840,8 @@ class VolumeGroupEditor: self.logvolstore.set_value(iter, 2, "%Ld" % (lv.size,)) - def updateVGSpaceLabels(self, alt_pvlist=None): - if alt_pvlist == None: - pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model()) - else: - pvlist = alt_pvlist - - (total, used, free) = self.computeSpaceValues(alt_pvlist=pvlist) + def updateVGSpaceLabels(self): + (total, used, free) = self.computeSpaceValues() self.totalSpaceLabel.set_text("%10.2f MB" % (total,)) self.usedSpaceLabel.set_text("%10.2f MB" % (used,)) @@ -900,18 +876,6 @@ class VolumeGroupEditor: return [] pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model()) - pesize = int(self.peCombo.get_active_value()) / 1024.0 - availSpaceMB = self.computeVGSize(pvlist, pesize) - neededSpaceMB = self.computeLVSpaceNeeded(self.vg.lvs) - - if neededSpaceMB > availSpaceMB: - self.intf.messageWindow(_("Not enough space"), - _("The logical volumes you have " - "configured require %d MB, but the " - "volume group only has %d MB. Please " - "either make the volume group larger " - "or make the logical volume(s) smaller.") % (neededSpaceMB, availSpaceMB), custom_icon="error") - continue # check volume name volname = self.volnameEntry.get_text().strip() @@ -1009,7 +973,7 @@ class VolumeGroupEditor: if not self.isNew: self.volnameEntry.set_text(self.vg.name) else: - self.volnameEntry.set_text(storage.suggestedVGName(anaconda.id.network)) + self.volnameEntry.set_text(self.storage.createSuggestedVGName(anaconda.id.network)) else: lbl = createAlignedLabel(_("Volume Group Name:")) self.volnameEntry = gtk.Label(self.vg.name) -- cgit From 1261e31956bb3f1ae392ff94b9857e7c287c885b Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 14:27:00 -0600 Subject: Add MAX_LV_SLOTS and to lvm.py and remove unused pesize from getMaxLVSize. --- storage/devicelibs/lvm.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py index bd39fc9b8..0faee10e8 100644 --- a/storage/devicelibs/lvm.py +++ b/storage/devicelibs/lvm.py @@ -31,6 +31,8 @@ from ..errors import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) +MAX_LV_SLOTS = 256 + def has_lvm(): has_lvm = False for path in os.environ["PATH"].split(":"): @@ -63,11 +65,8 @@ def getPossiblePhysicalExtents(floor=0): return possiblePE -def getMaxLVSize(pe): - """Given a PE size in KB, returns maximum size (in MB) of a logical volume. - - pe - PE size in KB - """ +def getMaxLVSize(): + """ Return the maximum size (in MB) of a logical volume. """ if iutil.getArch() in ("x86_64", "ppc64"): #64bit architectures return (8*1024*1024*1024*1024) #Max is 8EiB (very large number..) else: -- cgit From 5018a188747fadcdf2c9f545c97dff7ed7f80a93 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 14:28:39 -0600 Subject: Fix a bunch of "supported" methods so they work properly. --- storage/formats/fs.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 68bc681c9..c1eebe409 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -547,6 +547,9 @@ class FS(DeviceFormat): def supported(self): # we aren't checking for fsck because we shouldn't need it + if not self._supported: + return False + for prog in [self.mkfsProg, self.resizefsProg, self.labelfsProg]: if not prog: continue @@ -801,7 +804,7 @@ class BTRFS(FS): """ Is this filesystem a supported type? """ supported = self._supported if flags.cmdline.has_key("icantbelieveitsnotbtr"): - supported = True + supported = FS.supported(self) return supported @@ -825,7 +828,7 @@ class GFS2(FS): """ Is this filesystem a supported type? """ supported = self._supported if flags.cmdline.has_key("gfs2"): - supported = True + supported = FS.supported(self) return supported @@ -852,7 +855,7 @@ class JFS(FS): """ Is this filesystem a supported type? """ supported = self._supported if flags.cmdline.has_key("jfs"): - supported = True + supported = FS.supported(self) return supported -- cgit From 42c26bea1c28fb1defd4f911cb3b31108cc470d5 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 14:29:40 -0600 Subject: LUKS is not supported in the sense that _supported implies. The supported attr is only used for deciding which formats should be included in the "fstype" combo in partition/lvm/raid dialogs. In this sense, luks is not supported. --- storage/formats/luks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/formats/luks.py b/storage/formats/luks.py index a4a20d697..349f034f8 100644 --- a/storage/formats/luks.py +++ b/storage/formats/luks.py @@ -42,7 +42,7 @@ class LUKS(DeviceFormat): _name = "LUKS" _udevTypes = ["crypto_LUKS"] _formattable = True # can be formatted - _supported = True # is supported + _supported = False # is supported _linuxNative = True # for clearpart _packages = ["cryptsetup-luks"] # required packages -- cgit From 9998edd3e898b8cc72b60e686cec2a6a841a6e34 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 3 Mar 2009 15:44:12 -0500 Subject: Fix tracebacks when loading the "Change device" dialog. --- iw/bootloader_main_gui.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iw/bootloader_main_gui.py b/iw/bootloader_main_gui.py index b8991b40e..fda24c70e 100644 --- a/iw/bootloader_main_gui.py +++ b/iw/bootloader_main_gui.py @@ -94,12 +94,12 @@ class MainBootloaderWindow(InstallWindow): for disk in disks: size = disk.size - model = disk.partedDisk.device.model + m = disk.partedDisk.device.model i = model.append(None) - model[i] = ("%s %8.0f MB %s" %(disk.name, size, model), + model[i] = ("%s %8.0f MB %s" %(disk.name, size, m), "%s" %(disk.name,)) - if d == active: + if disk == active: combo.set_active_iter(i) return model -- cgit From 7249ed0f13ad75696421f58d74c400fe90ce6434 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 3 Mar 2009 13:51:20 -0500 Subject: Use the right attributes to build the bootloaderChoices dict. --- platform.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/platform.py b/platform.py index 4adc5d106..2c35a5cbf 100644 --- a/platform.py +++ b/platform.py @@ -83,11 +83,11 @@ class Platform(object): if not bootDev: return ret - if bootDev.device.getName() == "RAIDDevice": - ret["boot"] = (bootDev.path, N_("RAID Device")) + if bootDev.type == "mdarray": + ret["boot"] = (bootDev.name, N_("RAID Device")) ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) else: - ret["boot"] = (bootDev.device, N_("First sector of boot partition")) + ret["boot"] = (bootDev.name, N_("First sector of boot partition")) ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) return ret @@ -140,7 +140,7 @@ class EFI(Platform): if not bootDev: return ret - ret["boot"] = (bootDev.path, N_("EFI System Partition")) + ret["boot"] = (bootDev.name, N_("EFI System Partition")) return ret def checkBootRequest(self, req, diskset): @@ -246,11 +246,11 @@ class IPSeriesPPC(PPC): if not bootDev: return ret - if bootDev.device.getName() == "RAIDDevice": - ret["boot"] = (bootDev.path, N_("RAID Device")) + if bootDev.type == "mdarray": + ret["boot"] = (bootDev.name, N_("RAID Device")) ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) else: - ret["boot"] = (bootDev.device, N_("PPC PReP Boot")) + ret["boot"] = (bootDev.name, N_("PPC PReP Boot")) return ret @@ -295,11 +295,11 @@ class NewWorldPPC(PPC): if not bootDev: return ret - if bootDev.device.getName() == "RAIDDevice": - ret["boot"] = (bootDev.path, N_("RAID Device")) + if bootDev.type == "mdarray": + ret["boot"] = (bootDev.name, N_("RAID Device")) ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) else: - ret["boot"] = (bootDev.path, N_("Apple Bootstrap")) + ret["boot"] = (bootDev.name, N_("Apple Bootstrap")) for (n, device) in enumerate(self.anaconda.id.storage.partitions): if device.format.type == "hfs" and device.bootable and device.path != bootDev.path: ret["boot%d" % n] = (device.path, N_("Apple Bootstrap")) @@ -355,11 +355,11 @@ class X86(EFI): if not bootDev: return {} - if bootDev.device.getName() == "RAIDDevice": - ret["boot"] = (bootDev.path, N_("RAID Device")) + if bootDev.type == "mdarray": + ret["boot"] = (bootDev.name, N_("RAID Device")) ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) else: - ret["boot"] = (bootDev.device, N_("First sector of boot partition")) + ret["boot"] = (bootDev.name, N_("First sector of boot partition")) ret["mbr"] = (bl.drivelist[0], N_("Master Boot Record (MBR)")) return ret -- cgit From ddd94cfdd6ba2bf339ce7ee5424e27edf572adaa Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 3 Mar 2009 17:49:10 -0500 Subject: Don't call selectBootloader anymore. That method was removed. --- yuminstall.py | 1 - 1 file changed, 1 deletion(-) diff --git a/yuminstall.py b/yuminstall.py index d66aef67b..d5efee07f 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -1316,7 +1316,6 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon # New installs only - upgrades will already have all this stuff. self.selectBestKernel(anaconda) self.selectPackage(anaconda.platform.bootloaderPackage) - self.selectBootloader() self.selectFSPackages(anaconda.id.storage) self.selectAnacondaNeeds() -- cgit From 955380542f3c205c7edcb62731f69ec73be1b8a1 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 09:48:16 -0500 Subject: parents is an attribute on a Device class. --- storage/devices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devices.py b/storage/devices.py index 41bf0a1d7..320e23db5 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -282,7 +282,7 @@ class Device(object): """ packages = self._packages packages.extend(self.format.packages) - for parent in parents: + for parent in self.parents: for package in parent.packages: if package not in packages: packages.append(package) -- cgit From a87612a0deddc4fc160b13175e5c68f7717201da Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 09:49:40 -0500 Subject: protectedPartitions is a list, not a function. --- partedUtils.py | 4 ++-- yuminstall.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/partedUtils.py b/partedUtils.py index dc64ee3e0..c42dc6748 100644 --- a/partedUtils.py +++ b/partedUtils.py @@ -223,7 +223,7 @@ def hasProtectedPartitions(drive, anaconda): return rc try: - for protected in anaconda.id.partitions.protectedPartitions(): + for protected in anaconda.id.storage.protectedPartitions: if protected.startswith(drive): part = protected[len(drive):] if part[0] == "p": @@ -584,7 +584,7 @@ class DiskSet: drives = self.disks.keys() drives.sort() - protected = self.anaconda.id.partitions.protectedPartitions() + protected = self.anaconda.id.storage.protectedPartitions for drive in drives: disk = self.disks[drive] diff --git a/yuminstall.py b/yuminstall.py index d5efee07f..eb69e2bfc 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -1434,7 +1434,7 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon # If there are any protected partitions we want to mount, create their # mount points now. - protected = anaconda.id.storage.protectedPartitions() + protected = anaconda.id.storage.protectedPartitions if protected: for protectedDev in protected: request = anaconda.id.storage.devicetree.getDeviceByName(protectedDev) -- cgit From 4b45614b8b822f3a30270191501b15387af41ac2 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 10:05:04 -0500 Subject: Fix testing for whether a device requires networking. There's no more isNetdev method, so we'll have to use the type of the object as a test instead. --- network.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/network.py b/network.py index 3531de999..4a2eca9d5 100644 --- a/network.py +++ b/network.py @@ -585,8 +585,9 @@ class Network: # tell NM not to touch the interface(s) actually used for /, but we # have no logic to determine that if anaconda is not None: + import storage rootdev = anaconda.id.storage.fsset.rootDevice - if rootdev.isNetdev: + if isinstance(rootdev, storage.devices.NetworkDevice): f.write("NM_CONTROLLED=no\n") f.close() -- cgit From dc45f089c48665590890325e7aaebd67b2285c4a Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 10:12:29 -0500 Subject: Handle write and writeKS for the iSCSI and ZFCP modules. --- instdata.py | 3 ++- yuminstall.py | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/instdata.py b/instdata.py index c0aaf924d..990b125b3 100644 --- a/instdata.py +++ b/instdata.py @@ -294,7 +294,8 @@ class InstallData: self.security.writeKS(f) self.timezone.writeKS(f) self.bootloader.writeKS(f) - self.storage.writeKS(f) # FIXME: unimplemented + self.storage.iscsi.writeKS(f) + self.storage.zfcp.writeKS(f) if self.backend is not None: self.backend.writeKS(f) diff --git a/yuminstall.py b/yuminstall.py index eb69e2bfc..f30d7aa67 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -1497,8 +1497,8 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon shutil.copyfile("/etc/modprobe.d/anaconda", anaconda.rootPath + "/etc/modprobe.d/anaconda") anaconda.id.network.write(instPath=anaconda.rootPath, anaconda=anaconda) - anaconda.id.iscsi.write(anaconda.rootPath, anaconda) - anaconda.id.zfcp.write(anaconda.rootPath) + anaconda.id.storage.iscsi.write(anaconda.rootPath, anaconda) + anaconda.id.storage.zfcp.write(anaconda.rootPath) if not anaconda.id.isHeadless: anaconda.id.keyboard.write(anaconda.rootPath) -- cgit From 80e71234c8952f938964f3f9680e4b06f5d5201b Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 10:21:40 -0500 Subject: Handle mountopts being empty. We want to do the check for empty twice, since removing "defaults" and "defaults," may make the original mountopts empty. --- storage/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index cba5d0990..11e2e7b51 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1111,8 +1111,10 @@ class FSSet(object): continue if device.format.mountpoint: options = device.format.mountopts - options = options.replace("defaults,", "") - options = options.replace("defaults", "") + if options: + options = options.replace("defaults,", "") + options = options.replace("defaults", "") + if options: options = "rw," + options else: -- cgit From e7c78a0f7628c9413665faa16947a5f56babbdfc Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 11:30:25 -0500 Subject: Treat the values in the reqmedia dict as lists, not dicts themselves. I'm not sure how this code ever worked, but we are handling the values as lists in most places so that's likely what we always meant to be doing. --- sortedtransaction.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sortedtransaction.py b/sortedtransaction.py index e5b2f0276..d7384bbc6 100644 --- a/sortedtransaction.py +++ b/sortedtransaction.py @@ -50,12 +50,12 @@ class SplitMediaTransactionData(SortableTransactionData): return TransactionData.getMembers(self, pkgtup) if pkgtup is None: returnlist = [] - for key in self.reqmedia[self.curmedia]: - returnlist.extend(self.pkgdict[key]) + for ele in self.reqmedia[self.curmedia]: + returnlist.extend(self.pkgdict[ele]) return returnlist - if self.reqmedia[self.curmedia].has_key(pkgtup): + if pkgtup in self.reqmedia[self.curmedia]: return self.pkgdict[pkgtup] else: return [] -- cgit From a646faa1cba2ada37d9d17b6f7785c469f64cbec Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 12:01:38 -0500 Subject: Look for rootDevice in the right place. --- bootloader.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bootloader.py b/bootloader.py index a5e4a946d..22fb0e68d 100644 --- a/bootloader.py +++ b/bootloader.py @@ -136,7 +136,7 @@ def writeBootloader(anaconda): kernelList = [] otherList = [] - rootDev = getattr(anaconda.id.rootDevice, "path", None) + rootDev = getattr(anaconda.id.storage.fsset.rootDevice, "name", None) defaultDev = anaconda.id.bootloader.images.getDefault() kernelLabel = None -- cgit From f780409e2eb333365b38736037cf8958c0e7c430 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 14:20:07 -0500 Subject: Fix copying the storage.log file over to the installed system. --- packages.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages.py b/packages.py index 9d763896f..1de32a7a9 100644 --- a/packages.py +++ b/packages.py @@ -79,7 +79,8 @@ def copyAnacondaLogs(anaconda): log.info("Copying anaconda logs") for (fn, dest) in (("/tmp/anaconda.log", "anaconda.log"), ("/tmp/syslog", "anaconda.syslog"), - ("/tmp/X.log", "anaconda.xlog", "/tmp/storage.log")): + ("/tmp/X.log", "anaconda.xlog"), + ("/tmp/storage.log", "storage.log")): if os.access(fn, os.R_OK): try: shutil.copyfile(fn, "%s/var/log/%s" %(anaconda.rootPath, dest)) -- cgit From f6ccb426af374f745efe53cfbb1df1175e550de7 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 14:38:06 -0500 Subject: Fix grabbing the rootDevice in doMethodComplete. --- installmethod.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/installmethod.py b/installmethod.py index d62201583..f2c3d7191 100644 --- a/installmethod.py +++ b/installmethod.py @@ -47,7 +47,7 @@ def doMethodComplete(anaconda): isys.ejectCdrom(dev) mtab = "/dev/root / ext3 ro 0 0\n" - rootDevice = anaconda.id.storage.rootDevice + rootDevice = anaconda.id.storage.fsset.rootDevice if rootDevice: mtab = "/dev/root / %s ro 0 0\n" % rootDevice.format.type -- cgit From 9276f6795674a776f29ccbaa4ebb0ed2b5b2e25f Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Feb 2009 14:16:04 -0500 Subject: Add the existing booty back into anaconda. Welcome back, booty. We missed you. --- booty/bootloaderInfo.py | 2163 ++++++++++++++++++++++++++++++++++++++++++++++ booty/booty.py | 385 +++++++++ booty/checkbootloader.py | 270 ++++++ booty/lilo.py | 326 +++++++ 4 files changed, 3144 insertions(+) create mode 100644 booty/bootloaderInfo.py create mode 100644 booty/booty.py create mode 100644 booty/checkbootloader.py create mode 100644 booty/lilo.py diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py new file mode 100644 index 000000000..6d8add73a --- /dev/null +++ b/booty/bootloaderInfo.py @@ -0,0 +1,2163 @@ +# +# bootloaderInfo.py - bootloader config object used in creation of new +# bootloader configs. Originally from anaconda +# +# Jeremy Katz +# Erik Troan +# Peter Jones +# +# Copyright 2005-2008 Red Hat, Inc. +# +# This software may be freely redistributed under the terms of the GNU +# library public license. +# +# 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. +# + +import os, sys +import crypt +import random +import shutil +import string +import struct +from copy import copy + +from lilo import LiloConfigFile +import rhpl +#from rhpl.log import log +from rhpl.translate import _, N_ +import rhpl.executil + +import booty +import checkbootloader + +if rhpl.getArch() not in ("s390", "s390x"): + import block + +dosFilesystems = ('FAT', 'fat16', 'fat32', 'ntfs', 'hpfs') + +def doesDualBoot(): + if rhpl.getArch() == "i386" or rhpl.getArch() == "x86_64": + return 1 + return 0 + +def checkForBootBlock(device): + fd = os.open(device, os.O_RDONLY) + buf = os.read(fd, 512) + os.close(fd) + if len(buf) >= 512 and \ + struct.unpack("H", buf[0x1fe: 0x200]) == (0xaa55,): + return True + return False + +# hack and a half +# there's no guarantee that data is written to the disk and grub +# reads both the filesystem and the disk. suck. +def syncDataToDisk(dev, mntpt, instRoot = "/"): + import isys + isys.sync() + isys.sync() + isys.sync() + + # and xfs is even more "special" (#117968) + if isys.readFSType(dev) == "xfs": + rhpl.executil.execWithRedirect( "/usr/sbin/xfs_freeze", + ["/usr/sbin/xfs_freeze", "-f", mntpt], + stdout = "/dev/tty5", + stderr = "/dev/tty5", + root = instRoot) + rhpl.executil.execWithRedirect( "/usr/sbin/xfs_freeze", + ["/usr/sbin/xfs_freeze", "-u", mntpt], + stdout = "/dev/tty5", + stderr = "/dev/tty5", + root = instRoot) + +class BootyNoKernelWarning: + def __init__ (self, value=""): + self.value = value + + def __str__ (self): + return self.value + +class KernelArguments: + + def get(self): + return self.args + + def set(self, args): + self.args = args + + def chandevget(self): + return self.cargs + + def chandevset(self, args): + self.cargs = args + + def append(self, args): + if self.args: + # don't duplicate the addition of an argument (#128492) + if self.args.find(args) != -1: + return + self.args = self.args + " " + self.args = self.args + "%s" % (args,) + + + def __init__(self): + newArgs = [] + cfgFilename = "/tmp/install.cfg" + + if rhpl.getArch() == "s390": + self.cargs = [] + f = open(cfgFilename) + for line in f: + try: + (vname,vparm) = line.split('=', 1) + vname = vname.strip() + vparm = vparm.replace('"','') + vparm = vparm.strip() + if vname == "DASD": + newArgs.append("dasd=" + vparm) + if vname == "CHANDEV": + self.cargs.append(vparm) + if vname == "QETHPARM": + self.cargs.append(vparm) + except Exception, e: + pass + #log("exception parsing %s: %s" % (cfgFilename, e)) + f.close() + + # look for kernel arguments we know should be preserved and add them + ourargs = ["speakup_synth=", "apic", "noapic", "apm=", "ide=nodma", + "noht", "acpi=", "video=", "pci=", "nodmraid", "nompath"] + f = open("/proc/cmdline") + cmdline = f.read()[:-1] + f.close() + cmdlineargs = cmdline.split(" ") + for arg in cmdlineargs: + for check in ourargs: + if arg.startswith(check): + newArgs.append(arg) + + self.args = " ".join(newArgs) + + +class BootImages: + """A collection to keep track of boot images available on the system. + Examples would be: + ('linux', 'Red Hat Linux', 'ext2'), + ('Other', 'Other', 'fat32'), ... + """ + def __init__(self): + self.default = None + self.images = {} + + def getImages(self): + """returns dictionary of (label, longlabel, devtype) pairs + indexed by device""" + # return a copy so users can modify it w/o affecting us + return copy(self.images) + + + def setImageLabel(self, dev, label, setLong = 0): + orig = self.images[dev] + if setLong: + self.images[dev] = (orig[0], label, orig[2]) + else: + self.images[dev] = (label, orig[1], orig[2]) + + def setDefault(self, default): + # default is a device + self.default = default + + def getDefault(self): + return self.default + + # XXX this has internal anaconda-ish knowledge. ick + def setup(self, diskSet, fsset): + devices = {} + devs = self.availableBootDevices(diskSet, fsset) + for (dev, type) in devs: + devices[dev] = 1 + + # These partitions have disappeared + for dev in self.images.keys(): + if not devices.has_key(dev): del self.images[dev] + + # These have appeared + for (dev, type) in devs: + if not self.images.has_key(dev): + if type in dosFilesystems and doesDualBoot(): + self.images[dev] = ("Other", "Other", type) + elif type in ("hfs", "hfs+") and rhpl.getPPCMachine() == "PMac": + self.images[dev] = ("Other", "Other", type) + else: + self.images[dev] = (None, None, type) + + + if not self.images.has_key(self.default): + entry = fsset.getEntryByMountPoint('/') + self.default = entry.device.getDevice() + (label, longlabel, type) = self.images[self.default] + if not label: + self.images[self.default] = ("linux", + getProductName(), type) + + # XXX more internal anaconda knowledge + def availableBootDevices(self, diskSet, fsset): + devs = [] + foundDos = 0 + for (dev, type) in diskSet.partitionTypes(): + if type in dosFilesystems and not foundDos and doesDualBoot(): + import isys + import partedUtils + + part = partedUtils.get_partition_by_name(diskSet.disks, dev) + if part.native_type not in partedUtils.dosPartitionTypes: + continue + + try: + bootable = checkForBootBlock('/dev/' + dev) + devs.append((dev, type)) + foundDos = 1 + except Exception, e: + #log("exception checking %s: %s" %(dev, e)) + pass + elif ((type == 'ntfs' or type =='hpfs') and not foundDos + and doesDualBoot()): + devs.append((dev, type)) + # maybe questionable, but the first ntfs or fat is likely to + # be the correct one to boot with XP using ntfs + foundDos = 1 + elif type in ('hfs', 'hfs+') and rhpl.getPPCMachine() == "PMac": + import isys + import partedUtils + + part = partedUtils.get_partition_by_name(diskSet.disks, dev) + if partedUtils.get_flags(part) != "boot": + devs.append((dev, type)) + + slash = fsset.getEntryByMountPoint('/') + if not slash or not slash.device or not slash.fsystem: + raise ValueError, ("Trying to pick boot devices but do not have a " + "sane root partition. Aborting install.") + devs.append((slash.device.getDevice(), slash.fsystem.getName())) + + devs.sort() + + return devs + + + +class bootloaderInfo: + def getConfigFileName(self): + if not self._configname: + raise NotImplementedError + return self._configname + configname = property(getConfigFileName, None, None, \ + "bootloader config file name") + + def getConfigFileDir(self): + if not self._configdir: + raise NotImplementedError + return self._configdir + configdir = property(getConfigFileDir, None, None, \ + "bootloader config file directory") + + def getConfigFilePath(self): + return "%s/%s" % (self.configdir, self.configname) + configfile = property(getConfigFilePath, None, None, \ + "full path and name of the real config file") + + def setUseGrub(self, val): + pass + + def useGrub(self): + return self.useGrubVal + + def setForceLBA(self, val): + pass + + def setPassword(self, val, isCrypted = 1): + pass + + def getPassword(self): + pass + + def getDevice(self): + return self.device + + def setDevice(self, device): + self.device = device + + (dev, part) = getDiskPart(device) + if part is None: + self.defaultDevice = "mbr" + else: + self.defaultDevice = "partition" + + # XXX need to abstract out the requirement for a fsset to be able + # to get it "on the fly" on a running system as well as being able + # to get it how we do now from anaconda. probably by having the + # first thing in the chain create a "fsset" object that has the + # dictionary of mounted filesystems since that's what we care about + def getBootloaderConfig(self, instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev): + images = bl.images.getImages() + + # on upgrade read in the lilo config file + lilo = LiloConfigFile () + self.perms = 0600 + if os.access (instRoot + self.configfile, os.R_OK): + self.perms = os.stat(instRoot + self.configfile)[0] & 0777 + lilo.read (instRoot + self.configfile) + os.rename(instRoot + self.configfile, + instRoot + self.configfile + '.rpmsave') + # if it's an absolute symlink, just get it out of our way + elif (os.path.islink(instRoot + self.configfile) and + os.readlink(instRoot + self.configfile)[0] == '/'): + os.rename(instRoot + self.configfile, + instRoot + self.configfile + '.rpmsave') + + # Remove any invalid entries that are in the file; we probably + # just removed those kernels. + for label in lilo.listImages(): + (fsType, sl, path, other) = lilo.getImage(label) + if fsType == "other": continue + + if not os.access(instRoot + sl.getPath(), os.R_OK): + lilo.delImage(label) + + lilo.addEntry("prompt", replace = 0) + lilo.addEntry("timeout", self.timeout or "20", replace = 0) + + rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + if not rootDev: + raise RuntimeError, "Installing lilo, but there is no root device" + + if rootDev == defaultDev: + lilo.addEntry("default", kernelList[0][0]) + else: + lilo.addEntry("default", chainList[0][0]) + + for (label, longlabel, version) in kernelList: + kernelTag = "-" + version + kernelFile = self.kernelLocation + "vmlinuz" + kernelTag + + try: + lilo.delImage(label) + except IndexError, msg: + pass + + sl = LiloConfigFile(imageType = "image", path = kernelFile) + + initrd = booty.makeInitrd (kernelTag, instRoot) + + sl.addEntry("label", label) + if os.access (instRoot + initrd, os.R_OK): + sl.addEntry("initrd", "%sinitrd%s.img" %(self.kernelLocation, + kernelTag)) + + sl.addEntry("read-only") + + append = "%s" %(self.args.get(),) + realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + if rootIsDevice(realroot): + sl.addEntry("root", '/dev/' + rootDev) + else: + if len(append) > 0: + append = "%s root=%s" %(append,realroot) + else: + append = "root=%s" %(realroot,) + + if len(append) > 0: + sl.addEntry('append', '"%s"' % (append,)) + + lilo.addImage (sl) + + for (label, longlabel, device) in chainList: + if ((not label) or (label == "")): + continue + try: + (fsType, sl, path, other) = lilo.getImage(label) + lilo.delImage(label) + except IndexError: + sl = LiloConfigFile(imageType = "other", + path = "/dev/%s" %(device)) + sl.addEntry("optional") + + sl.addEntry("label", label) + lilo.addImage (sl) + + # Sanity check #1. There could be aliases in sections which conflict + # with the new images we just created. If so, erase those aliases + imageNames = {} + for label in lilo.listImages(): + imageNames[label] = 1 + + for label in lilo.listImages(): + (fsType, sl, path, other) = lilo.getImage(label) + if sl.testEntry('alias'): + alias = sl.getEntry('alias') + if imageNames.has_key(alias): + sl.delEntry('alias') + imageNames[alias] = 1 + + # Sanity check #2. If single-key is turned on, go through all of + # the image names (including aliases) (we just built the list) and + # see if single-key will still work. + if lilo.testEntry('single-key'): + singleKeys = {} + turnOff = 0 + for label in imageNames.keys(): + l = label[0] + if singleKeys.has_key(l): + turnOff = 1 + singleKeys[l] = 1 + if turnOff: + lilo.delEntry('single-key') + + return lilo + + def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfig, intf = None): + if len(kernelList) >= 1: + config = self.getBootloaderConfig(instRoot, fsset, bl, langs, + kernelList, chainList, + defaultDev) + config.write(instRoot + self.configfile, perms = self.perms) + else: + self.noKernelsWarn(intf) + + return "" + + # XXX in the future we also should do some validation on the config + # file that's already there + # XXX concept of the intf isn't very well defined outside of anaconda... + # probably should just pass back up an error + def noKernelsWarn(self, intf): + raise BootyNoKernelWarning + + def getArgList(self): + args = [] + + if self.defaultDevice is None: + args.append("--location=none") + return args + + args.append("--location=%s" % (self.defaultDevice,)) + args.append("--driveorder=%s" % (",".join(self.drivelist))) + + if self.args.get(): + args.append("--append=\"%s\"" %(self.args.get())) + + return args + + def writeKS(self, f): + f.write("bootloader") + for arg in self.getArgList(): + f.write(" " + arg) + f.write("\n") + + def createDriveList(self): + # create a drive list that we can use for drive mappings + # XXX has anaconda internals knowledge + import isys + drives = isys.hardDriveDict().keys() + drives.sort(isys.compareDrives) + + # now filter out all of the drives without media present + drives = filter(lambda x: isys.mediaPresent(x), drives) + + return drives + + def updateDriveList(self, sortedList=[]): + self._drivelist = self.createDriveList() + + # If we're given a sort order, make sure the drives listed in it + # are put at the head of the drivelist in that order. All other + # drives follow behind in whatever order they're found. + if sortedList != []: + revSortedList = sortedList + revSortedList.reverse() + + for i in revSortedList: + try: + ele = self._drivelist.pop(self._drivelist.index(i)) + self._drivelist.insert(0, ele) + except: + pass + + def _getDriveList(self): + if self._drivelist is not None: + return self._drivelist + self.updateDriveList() + return self._drivelist + def _setDriveList(self, val): + self._drivelist = val + drivelist = property(_getDriveList, _setDriveList) + + def __init__(self): + self.args = KernelArguments() + self.images = BootImages() + self.device = None + self.defaultDevice = None # XXX hack, used by kickstart + self.useGrubVal = 0 # only used on x86 + self._configdir = None + self._configname = None + self.kernelLocation = "/boot/" + self.forceLBA32 = 0 + self.password = None + self.pure = None + self.above1024 = 0 + self.timeout = None + + # this has somewhat strange semantics. if 0, act like a normal + # "install" case. if 1, update lilo.conf (since grubby won't do that) + # and then run lilo or grub only. + # XXX THIS IS A HACK. implementation details are only there for x86 + self.doUpgradeOnly = 0 + self.kickstart = 0 + + self._drivelist = None + + from flags import flags + if flags.serial != 0: + # now look at /proc/cmdline to pull any serial console + # args + f = open("/proc/cmdline", "r") + cmdline = f.read()[:-1] + f.close() + + options = "" + device = None + cmdlineargs = cmdline.split(" ") + for arg in cmdlineargs: + # found a console argument + if arg.startswith("console="): + (foo, console) = arg.split("=") + # the options are everything after the comma + comma = console.find(",") + if comma != -1: + options = console[comma:] + device = console[:comma] + else: + options = "" + device = console + + if device is None and rhpl.getArch() != "ia64": + self.serialDevice = "ttyS0" + self.serialOptions = "" + else: + self.serialDevice = device + # don't keep the comma in the options + self.serialOptions = options[1:] + + if self.serialDevice: + self.args.append("console=%s%s" %(self.serialDevice, options)) + self.serial = 1 + self.timeout = 5 + else: + self.serial = 0 + self.serialDevice = None + self.serialOptions = None + + if flags.virtpconsole is not None: + if flags.virtpconsole.startswith("/dev/"): + con = flags.virtpconsole[5:] + else: + con = flags.virtpconsole + self.args.append("console=%s" %(con,)) + + +class grubBootloaderInfo(bootloaderInfo): + def setPassword(self, val, isCrypted = 1): + if not val: + self.password = val + self.pure = val + return + + if isCrypted and self.useGrubVal == 0: + #log("requested crypted password with lilo; ignoring") + self.pure = None + return + elif isCrypted: + self.password = val + self.pure = None + else: + salt = "$1$" + saltLen = 8 + + saltchars = string.letters + string.digits + './' + for i in range(saltLen): + salt += random.choice(saltchars) + + self.password = crypt.crypt(val, salt) + self.pure = val + + def getPassword (self): + return self.pure + + def setForceLBA(self, val): + self.forceLBA32 = val + + def setUseGrub(self, val): + self.useGrubVal = val + + def getPhysicalDevices(self, device): + # This finds a list of devices on which the given device name resides. + # Accepted values for "device" are raid1 md devices (i.e. "md0"), + # physical disks ("hda"), and real partitions on physical disks + # ("hda1"). Volume groups/logical volumes are not accepted. + # + # XXX this has internal anaconda-ish knowledge. ick. + import isys + import lvm + + if string.split(device, '/', 1)[0] in map (lambda vg: vg[0], + lvm.vglist()): + return [] + + if device.startswith("mapper/luks-"): + return [] + + if device.startswith('md'): + bootable = 0 + parts = checkbootloader.getRaidDisks(device, 1, stripPart=0) + parts.sort() + return parts + + return [device] + + def runGrubInstall(self, instRoot, bootDev, cmds, cfPath): + #log("GRUB commands:") + #for cmd in cmds: + # log("\t%s\n", cmd) + if cfPath == "/": + syncDataToDisk(bootDev, "/boot", instRoot) + else: + syncDataToDisk(bootDev, "/", instRoot) + + # copy the stage files over into /boot + rhpl.executil.execWithRedirect( "/sbin/grub-install", + ["/sbin/grub-install", "--just-copy"], + stdout = "/dev/tty5", stderr = "/dev/tty5", + root = instRoot) + + # really install the bootloader + for cmd in cmds: + p = os.pipe() + os.write(p[1], cmd + '\n') + os.close(p[1]) + import time + + # FIXME: hack to try to make sure everything is written + # to the disk + if cfPath == "/": + syncDataToDisk(bootDev, "/boot", instRoot) + else: + syncDataToDisk(bootDev, "/", instRoot) + + rhpl.executil.execWithRedirect('/sbin/grub' , + [ "grub", "--batch", "--no-floppy", + "--device-map=/boot/grub/device.map" ], + stdin = p[0], + stdout = "/dev/tty5", stderr = "/dev/tty5", + root = instRoot) + os.close(p[0]) + + def installGrub(self, instRoot, bootDevs, grubTarget, grubPath, fsset, + target, cfPath): + args = "--stage2=/boot/grub/stage2 " + if self.forceLBA32: + args = "%s--force-lba " % (args,) + + cmds = [] + for bootDev in bootDevs: + gtPart = self.getMatchingPart(bootDev, grubTarget) + gtDisk = self.grubbyPartitionName(getDiskPart(gtPart)[0]) + bPart = self.grubbyPartitionName(bootDev) + cmd = "root %s\n" % (bPart,) + + stage1Target = gtDisk + if target == "partition": + stage1Target = self.grubbyPartitionName(gtPart) + + cmd += "install %s%s/stage1 d %s %s/stage2 p %s%s/grub.conf" % \ + (args, grubPath, stage1Target, grubPath, bPart, grubPath) + cmds.append(cmd) + + self.runGrubInstall(instRoot, bootDev, cmds, cfPath) + + def writeGrub(self, instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfigFile): + + images = bl.images.getImages() + rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + + # XXX old config file should be read here for upgrade + + cf = "%s%s" % (instRoot, self.configfile) + self.perms = 0600 + if os.access (cf, os.R_OK): + self.perms = os.stat(cf)[0] & 0777 + os.rename(cf, cf + '.rpmsave') + + grubTarget = bl.getDevice() + target = "mbr" + if (grubTarget.startswith('rd/') or grubTarget.startswith('ida/') or + grubTarget.startswith('cciss/') or + grubTarget.startswith('sx8/') or + grubTarget.startswith('mapper/')): + if grubTarget[-1].isdigit(): + if grubTarget[-2] == 'p' or \ + (grubTarget[-2].isdigit() and grubTarget[-3] == 'p'): + target = "partition" + elif grubTarget[-1].isdigit() and not grubTarget.startswith('md'): + target = "partition" + + f = open(cf, "w+") + + f.write("# grub.conf generated by anaconda\n") + f.write("#\n") + f.write("# Note that you do not have to rerun grub " + "after making changes to this file\n") + + bootDev = fsset.getEntryByMountPoint("/boot") + grubPath = "/grub" + cfPath = "/" + if not bootDev: + bootDev = fsset.getEntryByMountPoint("/") + grubPath = "/boot/grub" + cfPath = "/boot/" + f.write("# NOTICE: You do not have a /boot partition. " + "This means that\n") + f.write("# all kernel and initrd paths are relative " + "to /, eg.\n") + else: + f.write("# NOTICE: You have a /boot partition. This means " + "that\n") + f.write("# all kernel and initrd paths are relative " + "to /boot/, eg.\n") + + bootDevs = self.getPhysicalDevices(bootDev.device.getDevice()) + bootDev = bootDev.device.getDevice() + + f.write('# root %s\n' % self.grubbyPartitionName(bootDevs[0])) + f.write("# kernel %svmlinuz-version ro " + "root=/dev/%s\n" % (cfPath, rootDev)) + f.write("# initrd %sinitrd-version.img\n" % (cfPath)) + f.write("#boot=/dev/%s\n" % (grubTarget)) + + # get the default image to boot... we have to walk and find it + # since grub indexes by where it is in the config file + if defaultDev == rootDev: + default = 0 + else: + # if the default isn't linux, it's the first thing in the + # chain list + default = len(kernelList) + + # keep track of which devices are used for the device.map + usedDevs = {} + + f.write('default=%s\n' % (default)) + f.write('timeout=%d\n' % (self.timeout or 0)) + + if self.serial == 1: + # grub the 0-based number of the serial console device + unit = self.serialDevice[-1] + + # and we want to set the speed too + speedend = 0 + for char in self.serialOptions: + if char not in string.digits: + break + speedend = speedend + 1 + if speedend != 0: + speed = self.serialOptions[:speedend] + else: + # reasonable default + speed = "9600" + + f.write("serial --unit=%s --speed=%s\n" %(unit, speed)) + f.write("terminal --timeout=%s serial console\n" % (self.timeout or 5)) + else: + # we only want splashimage if they're not using a serial console + if os.access("%s/boot/grub/splash.xpm.gz" %(instRoot,), os.R_OK): + f.write('splashimage=%s%sgrub/splash.xpm.gz\n' + % (self.grubbyPartitionName(bootDevs[0]), cfPath)) + f.write("hiddenmenu\n") + + for dev in self.getPhysicalDevices(grubTarget): + usedDevs[dev] = 1 + + if self.password: + f.write('password --md5 %s\n' %(self.password)) + + for (label, longlabel, version) in kernelList: + kernelTag = "-" + version + kernelFile = "%svmlinuz%s" % (cfPath, kernelTag) + + initrd = booty.makeInitrd (kernelTag, instRoot) + + f.write('title %s (%s)\n' % (longlabel, version)) + f.write('\troot %s\n' % self.grubbyPartitionName(bootDevs[0])) + + realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + realroot = " root=%s" %(realroot,) + + if version.endswith("xen0") or (version.endswith("xen") and not os.path.exists("/proc/xen")): + # hypervisor case + sermap = { "ttyS0": "com1", "ttyS1": "com2", + "ttyS2": "com3", "ttyS3": "com4" } + if self.serial and sermap.has_key(self.serialDevice) and \ + self.serialOptions: + hvs = "%s=%s" %(sermap[self.serialDevice], + self.serialOptions) + else: + hvs = "" + if version.endswith("xen0"): + hvFile = "%sxen.gz-%s %s" %(cfPath, + version.replace("xen0", ""), + hvs) + else: + hvFile = "%sxen.gz-%s %s" %(cfPath, + version.replace("xen", ""), + hvs) + f.write('\tkernel %s\n' %(hvFile,)) + f.write('\tmodule %s ro%s' %(kernelFile, realroot)) + if self.args.get(): + f.write(' %s' % self.args.get()) + f.write('\n') + + if os.access (instRoot + initrd, os.R_OK): + f.write('\tmodule %sinitrd%s.img\n' % (cfPath, kernelTag)) + else: # normal kernel + f.write('\tkernel %s ro%s' % (kernelFile, realroot)) + if self.args.get(): + f.write(' %s' % self.args.get()) + f.write('\n') + + if os.access (instRoot + initrd, os.R_OK): + f.write('\tinitrd %sinitrd%s.img\n' % (cfPath, kernelTag)) + + for (label, longlabel, device) in chainList: + if ((not longlabel) or (longlabel == "")): + continue + f.write('title %s\n' % (longlabel)) + f.write('\trootnoverify %s\n' % self.grubbyPartitionName(device)) +# f.write('\tmakeactive\n') + f.write('\tchainloader +1') + f.write('\n') + usedDevs[device] = 1 + + f.close() + + if not "/efi/" in cf: + os.chmod(cf, self.perms) + + try: + # make symlink for menu.lst (default config file name) + menulst = "%s%s/menu.lst" % (instRoot, self.configdir) + if os.access (menulst, os.R_OK): + os.rename(menulst, menulst + ".rpmsave") + os.symlink("./grub.conf", menulst) + except: + pass + + try: + # make symlink for /etc/grub.conf (config files belong in /etc) + etcgrub = "%s%s" % (instRoot, "/etc/grub.conf") + if os.access (etcgrub, os.R_OK): + os.rename(etcgrub, etcgrub + ".rpmsave") + os.symlink(".." + self.configfile, etcgrub) + except: + pass + + for dev in self.getPhysicalDevices(rootDev) + bootDevs: + usedDevs[dev] = 1 + + if os.access(instRoot + "/boot/grub/device.map", os.R_OK): + os.rename(instRoot + "/boot/grub/device.map", + instRoot + "/boot/grub/device.map.rpmsave") + if 1: # not os.access(instRoot + "/boot/grub/device.map", os.R_OK): + f = open(instRoot + "/boot/grub/device.map", "w+") + f.write("# this device map was generated by anaconda\n") + devs = usedDevs.keys() + usedDevs = {} + for dev in devs: + drive = getDiskPart(dev)[0] + if usedDevs.has_key(drive): + continue + usedDevs[drive] = 1 + devs = usedDevs.keys() + devs.sort() + for drive in devs: + # XXX hack city. If they're not the sort of thing that'll + # be in the device map, they shouldn't still be in the list. + if not drive.startswith('md'): + f.write("(%s) /dev/%s\n" % (self.grubbyDiskName(drive), + drive)) + f.close() + + sysconf = '/etc/sysconfig/grub' + if os.access (instRoot + sysconf, os.R_OK): + self.perms = os.stat(instRoot + sysconf)[0] & 0777 + os.rename(instRoot + sysconf, + instRoot + sysconf + '.rpmsave') + # if it's an absolute symlink, just get it out of our way + elif (os.path.islink(instRoot + sysconf) and + os.readlink(instRoot + sysconf)[0] == '/'): + os.rename(instRoot + sysconf, + instRoot + sysconf + '.rpmsave') + f = open(instRoot + sysconf, 'w+') + f.write("boot=/dev/%s\n" %(grubTarget,)) + # XXX forcelba never gets read back... + if self.forceLBA32: + f.write("forcelba=1\n") + else: + f.write("forcelba=0\n") + f.close() + + if not justConfigFile: + self.installGrub(instRoot, bootDevs, grubTarget, grubPath, fsset, \ + target, cfPath) + + return "" + + def getMatchingPart(self, bootDev, target): + bootName, bootPartNum = getDiskPart(bootDev) + devices = self.getPhysicalDevices(target) + for device in devices: + name, partNum = getDiskPart(device) + if name == bootName: + return device + return devices[0] + + def grubbyDiskName(self, name): + return "hd%d" % self.drivelist.index(name) + + def grubbyPartitionName(self, dev): + (name, partNum) = getDiskPart(dev) + if partNum != None: + return "(%s,%d)" % (self.grubbyDiskName(name), partNum) + else: + return "(%s)" %(self.grubbyDiskName(name)) + + + def getBootloaderConfig(self, instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev): + config = bootloaderInfo.getBootloaderConfig(self, instRoot, fsset, + bl, langs, + kernelList, chainList, + defaultDev) + + liloTarget = bl.getDevice() + + config.addEntry("boot", '/dev/' + liloTarget, replace = 0) + config.addEntry("map", "/boot/map", replace = 0) + config.addEntry("install", "/boot/boot.b", replace = 0) + message = "/boot/message" + + if self.pure is not None and not self.useGrubVal: + config.addEntry("restricted", replace = 0) + config.addEntry("password", self.pure, replace = 0) + + + import language + for lang in language.expandLangs(langs.getDefault()): + fn = "/boot/message." + lang + if os.access(instRoot + fn, os.R_OK): + message = fn + break + + if self.serial == 1: + # grab the 0-based number of the serial console device + unit = self.serialDevice[-1] + # FIXME: we should probably put some options, but lilo + # only supports up to 9600 baud so just use the defaults + # it's better than nothing :( + config.addEntry("serial=%s" %(unit,)) + else: + # message screws up serial console + if os.access(instRoot + message, os.R_OK): + config.addEntry("message", message, replace = 0) + + if not config.testEntry('lba32'): + if self.forceLBA32 or (bl.above1024 and + rhpl.getArch() != "x86_64"): + config.addEntry("lba32", replace = 0) + + return config + + # this is a hackish function that depends on the way anaconda writes + # out the grub.conf with a #boot= comment + # XXX this falls into the category of self.doUpgradeOnly + def upgradeGrub(self, instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfigFile): + if justConfigFile: + return "" + + theDev = None + for (fn, stanza) in [ ("/etc/sysconfig/grub", "boot="), + ("/boot/grub/grub.conf", "#boot=") ]: + try: + f = open(instRoot + fn, "r") + except: + continue + + # the following bits of code are straight from checkbootloader.py + lines = f.readlines() + f.close() + for line in lines: + if line.startswith(stanza): + theDev = checkbootloader.getBootDevString(line) + break + if theDev is not None: + break + + if theDev is None: + # we could find the dev before, but can't now... cry about it + return "" + + # migrate info to /etc/sysconfig/grub + self.writeSysconfig(instRoot, theDev) + + # more suckage. grub-install can't work without a valid /etc/mtab + # so we have to do shenanigans to get updated grub installed... + # steal some more code above + bootDev = fsset.getEntryByMountPoint("/boot") + grubPath = "/grub" + cfPath = "/" + if not bootDev: + bootDev = fsset.getEntryByMountPoint("/") + grubPath = "/boot/grub" + cfPath = "/boot/" + + masterBootDev = bootDev.device.getDevice(asBoot = 0) + if masterBootDev[0:2] == 'md': + rootDevs = checkbootloader.getRaidDisks(masterBootDev, raidLevel=1, + stripPart = 0) + else: + rootDevs = [masterBootDev] + + if theDev[5:7] == 'md': + stage1Devs = checkbootloader.getRaidDisks(theDev[5:], raidLevel=1) + else: + stage1Devs = [theDev[5:]] + + for stage1Dev in stage1Devs: + # cross fingers; if we can't find a root device on the same + # hardware as this boot device, we just blindly hope the first + # thing in the list works. + + grubbyStage1Dev = self.grubbyPartitionName(stage1Dev) + + grubbyRootPart = self.grubbyPartitionName(rootDevs[0]) + + for rootDev in rootDevs: + testGrubbyRootDev = getDiskPart(rootDev)[0] + testGrubbyRootDev = self.grubbyPartitionName(testGrubbyRootDev) + + if grubbyStage1Dev == testGrubbyRootDev: + grubbyRootPart = self.grubbyPartitionName(rootDev) + break + + args = "--stage2=/boot/grub/stage2 " + cmd ="root %s" % (grubbyRootPart,) + cmds = [ cmd ] + cmd = "install %s%s/stage1 d %s %s/stage2 p %s%s/grub.conf" \ + % (args, grubPath, grubbyStage1Dev, grubPath, grubbyRootPart, + grubPath) + cmds.append(cmd) + + if not justConfigFile: + #log("GRUB command %s", cmd) + self.runGrubInstall(instRoot, bootDev.device.setupDevice(), + cmds, cfPath) + + return "" + + def writeSysconfig(self, instRoot, installDev): + sysconf = '/etc/sysconfig/grub' + if not os.access(instRoot + sysconf, os.R_OK): + f = open(instRoot + sysconf, "w+") + f.write("boot=%s\n" %(installDev,)) + # XXX forcelba never gets read back at all... + if self.forceLBA32: + f.write("forcelba=1\n") + else: + f.write("forcelba=0\n") + f.close() + + def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfig, intf): + if self.timeout is None and chainList: + self.timeout = 5 + + # XXX HACK ALERT - see declaration above + if self.doUpgradeOnly: + if self.useGrubVal: + self.upgradeGrub(instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev, justConfig) + return + + if len(kernelList) < 1: + self.noKernelsWarn(intf) + + out = self.writeGrub(instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev, + justConfig | (not self.useGrubVal)) + + + def getArgList(self): + args = bootloaderInfo.getArgList(self) + + if self.forceLBA32: + args.append("--lba32") + if self.password: + args.append("--md5pass=%s" %(self.password)) + + return args + + def __init__(self): + bootloaderInfo.__init__(self) + self._configdir = "/boot/grub" + self._configname = "grub.conf" + # XXX use checkbootloader to determine what to default to + self.useGrubVal = 1 + self.kernelLocation = "/boot/" + self.password = None + self.pure = None + + +class efiBootloaderInfo(bootloaderInfo): + def isEfi(self): + return os.access("/sys/firmware/efi", os.R_OK) + + def getBootloaderName(self): + return self._bootloader + bootloader = property(getBootloaderName, None, None, \ + "name of the bootloader to install") + + # XXX wouldn't it be nice to have a real interface to use efibootmgr from? + def removeOldEfiEntries(self, instRoot): + p = os.pipe() + rhpl.executil.execWithRedirect('/usr/sbin/efibootmgr', ["efibootmgr"], + root = instRoot, stdout = p[1]) + os.close(p[1]) + + c = os.read(p[0], 1) + buf = c + while (c): + c = os.read(p[0], 1) + buf = buf + c + os.close(p[0]) + lines = string.split(buf, '\n') + for line in lines: + fields = string.split(line) + if len(fields) < 2: + continue + if string.join(fields[1:], " ") == getProductName(): + entry = fields[0][4:8] + rhpl.executil.execWithRedirect('/usr/sbin/efibootmgr', + ["efibootmgr", "-b", entry, "-B"], + root = instRoot, + stdout="/dev/tty5", stderr="/dev/tty5") + + def addNewEfiEntry(self, instRoot, fsset): + bootdev = fsset.getEntryByMountPoint("/boot/efi").device.getDevice() + if not bootdev: + bootdev = fsset.getEntryByDeviceName("sda1").device.getDevice() + + link = "%s%s/%s" % (instRoot, "/etc/", self.configname) + if not os.access(link, os.R_OK): + os.symlink("../%s" % (self.configfile), link) + + ind = len(bootdev) + try: + while (bootdev[ind-1] in string.digits): + ind = ind - 1 + except IndexError: + ind = len(bootdev) - 1 + + bootdisk = bootdev[:ind] + bootpart = bootdev[ind:] + if (bootdisk.startswith('ida/') or bootdisk.startswith('cciss/') or + bootdisk.startswith('rd/') or bootdisk.startswith('sx8/')): + bootdisk = bootdisk[:-1] + + argv = [ "/usr/sbin/efibootmgr", "-c" , "-w", "-L", + getProductName(), "-d", "/dev/%s" % bootdisk, + "-p", bootpart, "-l", "\\EFI\\redhat\\" + self.bootloader ] + rhpl.executil.execWithRedirect(argv[0], argv, root = instRoot, + stdout = "/dev/tty5", + stderr = "/dev/tty5") + + def installGrub(self, instRoot, bootDevs, grubTarget, grubPath, fsset, + target, cfPath): + if not self.isEfi(): + raise EnvironmentError + self.removeOldEfiEntries(instRoot) + self.addNewEfiEntry(instRoot, fsset) + + def __init__(self, initialize = True): + if initialize: + bootloaderInfo.__init__(self) + if self.isEfi(): + self._configdir = "/boot/efi/EFI/redhat" + self._configname = "grub.conf" + self._bootloader = "grub.efi" + self.useGrubVal = 1 + self.kernelLocation = "" + +class x86BootloaderInfo(grubBootloaderInfo, efiBootloaderInfo): + def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfig, intf): + grubBootloaderInfo.write(self, instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev, justConfig, intf) + + # XXX move the lilo.conf out of the way if they're using GRUB + # so that /sbin/installkernel does a more correct thing + if self.useGrubVal and os.access(instRoot + '/etc/lilo.conf', os.R_OK): + os.rename(instRoot + "/etc/lilo.conf", + instRoot + "/etc/lilo.conf.anaconda") + + def installGrub(self, *args): + args = [self] + list(args) + try: + apply(efiBootloaderInfo.installGrub, args, {}) + except EnvironmentError: + apply(grubBootloaderInfo.installGrub, args, {}) + + def __init__(self): + grubBootloaderInfo.__init__(self) + efiBootloaderInfo.__init__(self, initialize=False) + +class ia64BootloaderInfo(efiBootloaderInfo): + def getBootloaderConfig(self, instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev): + config = bootloaderInfo.getBootloaderConfig(self, instRoot, fsset, + bl, langs, + kernelList, chainList, + defaultDev) + # altix boxes need relocatable (#120851) + config.addEntry("relocatable") + + return config + + def writeLilo(self, instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev, justConfig): + config = self.getBootloaderConfig(instRoot, fsset, bl, langs, + kernelList, chainList, defaultDev) + config.write(instRoot + self.configfile, perms = 0755) + + return "" + + def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfig, intf): + if len(kernelList) >= 1: + out = self.writeLilo(instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev, justConfig) + else: + self.noKernelsWarn(intf) + + self.removeOldEfiEntries(instRoot) + self.addNewEfiEntry(instRoot, fsset) + + def __init__(self): + efiBootloaderInfo.__init__(self) + self._configname = "elilo.conf" + self._bootloader = "elilo.efi" + +class s390BootloaderInfo(bootloaderInfo): + def getBootloaderConfig(self, instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev): + images = bl.images.getImages() + + # on upgrade read in the lilo config file + lilo = LiloConfigFile () + self.perms = 0600 + if os.access (instRoot + self.configfile, os.R_OK): + self.perms = os.stat(instRoot + self.configfile)[0] & 0777 + lilo.read (instRoot + self.configfile) + os.rename(instRoot + self.configfile, + instRoot + self.configfile + '.rpmsave') + + # Remove any invalid entries that are in the file; we probably + # just removed those kernels. + for label in lilo.listImages(): + (fsType, sl, path, other) = lilo.getImage(label) + if fsType == "other": continue + + if not os.access(instRoot + sl.getPath(), os.R_OK): + lilo.delImage(label) + + rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + if not rootDev: + raise RuntimeError, "Installing zipl, but there is no root device" + + if rootDev == defaultDev: + lilo.addEntry("default", kernelList[0][0]) + else: + lilo.addEntry("default", chainList[0][0]) + + for (label, longlabel, version) in kernelList: + kernelTag = "-" + version + kernelFile = self.kernelLocation + "vmlinuz" + kernelTag + + try: + lilo.delImage(label) + except IndexError, msg: + pass + + sl = LiloConfigFile(imageType = "image", path = kernelFile) + + initrd = booty.makeInitrd (kernelTag, instRoot) + + sl.addEntry("label", label) + if os.access (instRoot + initrd, os.R_OK): + sl.addEntry("initrd", + "%sinitrd%s.img" %(self.kernelLocation, kernelTag)) + + sl.addEntry("read-only") + sl.addEntry("root", '/dev/' + rootDev) + sl.addEntry("ipldevice", '/dev/' + rootDev[:-1]) + + if self.args.get(): + sl.addEntry('append', '"%s"' % self.args.get()) + + lilo.addImage (sl) + + for (label, longlabel, device) in chainList: + if ((not label) or (label == "")): + continue + try: + (fsType, sl, path, other) = lilo.getImage(label) + lilo.delImage(label) + except IndexError: + sl = LiloConfigFile(imageType = "other", + path = "/dev/%s" %(device)) + sl.addEntry("optional") + + sl.addEntry("label", label) + lilo.addImage (sl) + + # Sanity check #1. There could be aliases in sections which conflict + # with the new images we just created. If so, erase those aliases + imageNames = {} + for label in lilo.listImages(): + imageNames[label] = 1 + + for label in lilo.listImages(): + (fsType, sl, path, other) = lilo.getImage(label) + if sl.testEntry('alias'): + alias = sl.getEntry('alias') + if imageNames.has_key(alias): + sl.delEntry('alias') + imageNames[alias] = 1 + + # Sanity check #2. If single-key is turned on, go through all of + # the image names (including aliases) (we just built the list) and + # see if single-key will still work. + if lilo.testEntry('single-key'): + singleKeys = {} + turnOff = 0 + for label in imageNames.keys(): + l = label[0] + if singleKeys.has_key(l): + turnOff = 1 + singleKeys[l] = 1 + if turnOff: + lilo.delEntry('single-key') + + return lilo + + def writeChandevConf(self, bl, instroot): # S/390 only + cf = "/etc/chandev.conf" + self.perms = 0644 + if bl.args.chandevget(): + fd = os.open(instroot + "/etc/chandev.conf", + os.O_WRONLY | os.O_CREAT) + os.write(fd, "noauto\n") + for cdev in bl.args.chandevget(): + os.write(fd,'%s\n' % cdev) + os.close(fd) + return "" + + + def writeZipl(self, instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfigFile): + images = bl.images.getImages() + rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + + cf = '/etc/zipl.conf' + self.perms = 0600 + if os.access (instRoot + cf, os.R_OK): + self.perms = os.stat(instRoot + cf)[0] & 0777 + os.rename(instRoot + cf, + instRoot + cf + '.rpmsave') + + f = open(instRoot + cf, "w+") + + f.write('[defaultboot]\n') + f.write('default=' + kernelList[0][0] + '\n') + f.write('target=%s\n' % (self.kernelLocation)) + + cfPath = "/boot/" + for (label, longlabel, version) in kernelList: + kernelTag = "-" + version + kernelFile = "%svmlinuz%s" % (cfPath, kernelTag) + + initrd = booty.makeInitrd (kernelTag, instRoot) + f.write('[%s]\n' % (label)) + f.write('\timage=%s\n' % (kernelFile)) + if os.access (instRoot + initrd, os.R_OK): + f.write('\tramdisk=%sinitrd%s.img\n' %(self.kernelLocation, + kernelTag)) + realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + f.write('\tparameters="root=%s' %(realroot,)) + if bl.args.get(): + f.write(' %s' % (bl.args.get())) + f.write('"\n') + + f.close() + + if not justConfigFile: + argv = [ "/sbin/zipl" ] + rhpl.executil.execWithRedirect(argv[0], argv, root = instRoot, + stdout = "/dev/stdout", + stderr = "/dev/stderr") + + return "" + + def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfig, intf): + out = self.writeZipl(instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev, + justConfig | (not self.useZiplVal)) + out = self.writeChandevConf(bl, instRoot) + + def __init__(self): + bootloaderInfo.__init__(self) + self.useZiplVal = 1 # only used on s390 + self.kernelLocation = "/boot/" + self.configfile = "/etc/zipl.conf" + + +class alphaBootloaderInfo(bootloaderInfo): + def wholeDevice (self, path): + (device, foo) = getDiskPart(path) + return device + + def partitionNum (self, path): + # getDiskPart returns part numbers 0-based; we need it one based + # *sigh* + (foo, partitionNumber) = getDiskPart(path) + return partitionNumber + 1 + + # See if we would have to use MILO. MILO isn't supported by Red Hat. + def useMilo (self): + try: + f = open ('/proc/cpuinfo', 'ro') + except: + return + lines = f.readlines () + f.close() + serial = "" + for line in lines: + if line.find("system serial number") != -1: + serial = string.strip (string.split (line, ':')[1]) + break + + if serial and len (serial) >= 4 and serial.startswith("MILO"): + return 1 + else: + return 0 + + def writeAboot(self, instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev, justConfig): + # Get bootDevice and rootDevice + rootDevice = fsset.getEntryByMountPoint("/").device.getDevice() + if fsset.getEntryByMountPoint("/boot"): + bootDevice = fsset.getEntryByMountPoint("/boot").device.getDevice() + else: + bootDevice = rootDevice + bootnotroot = bootDevice != rootDevice + + # If /etc/aboot.conf already exists we rename it + # /etc/aboot.conf.rpmsave. + if os.path.isfile(instRoot + self.configfile): + os.rename (instRoot + self.configfile, + instRoot + self.configfile + ".rpmsave") + + # Then we create the necessary files. If the root device isn't + # the boot device, we create /boot/etc/ where the aboot.conf + # will live, and we create /etc/aboot.conf as a symlink to it. + if bootnotroot: + # Do we have /boot/etc ? If not, create one + if not os.path.isdir (instRoot + '/boot/etc'): + os.mkdir(instRoot + '/boot/etc', 0755) + + # We install the symlink (/etc/aboot.conf has already been + # renamed in necessary.) + os.symlink("../boot" + self.configfile, instRoot + self.configfile) + + cfPath = instRoot + "/boot" + self.configfile + # Kernel path is set to / because a boot partition will + # be a root on its own. + kernelPath = '/' + # Otherwise, we just need to create /etc/aboot.conf. + else: + cfPath = instRoot + self.configfile + kernelPath = self.kernelLocation + + # If we already have an aboot.conf, rename it + if os.access (cfPath, os.R_OK): + self.perms = os.stat(cfPath)[0] & 0777 + os.rename(cfPath, cfPath + '.rpmsave') + + # Now we're going to create and populate cfPath. + f = open (cfPath, 'w+') + f.write ("# aboot default configurations\n") + + if bootnotroot: + f.write ("# NOTICE: You have a /boot partition. This means that\n") + f.write ("# all kernel paths are relative to /boot/\n") + + # bpn is the boot partition number. + bpn = self.partitionNum(bootDevice) + lines = 0 + + # We write entries line using the following format: + # root= [options] + # We get all the kernels we need to know about in kernelList. + + for (kernel, tag, version) in kernelList: + kernelTag = "-" + version + kernelFile = "%svmlinuz%s" %(kernelPath, kernelTag) + + f.write("%d:%d%s" %(lines, bpn, kernelFile)) + + # See if we can come up with an initrd argument that exists + initrd = booty.makeInitrd (kernelTag, instRoot) + if os.path.isfile(instRoot + initrd): + f.write(" initrd=%sinitrd%s.img" %(kernelPath, kernelTag)) + + realroot = getRootDevName(initrd, fsset, rootDevice, instRoot) + f.write(" root=%s" %(realroot,)) + + args = self.args.get() + if args: + f.write(" %s" %(args,)) + + f.write("\n") + lines = lines + 1 + + # We're done writing the file + f.close () + del f + + if not justConfig: + # Now we're ready to write the relevant boot information. wbd + # is the whole boot device, bdpn is the boot device partition + # number. + wbd = self.wholeDevice (bootDevice) + bdpn = self.partitionNum (bootDevice) + + # Calling swriteboot. The first argument is the disk to write + # to and the second argument is a path to the bootstrap loader + # file. + args = ("swriteboot", ("/dev/%s" % wbd), "/boot/bootlx") + #log("swriteboot command: %s" %(args,)) + rhpl.executil.execWithRedirect ('/sbin/swriteboot', args, + root = instRoot, + stdout = "/dev/tty5", + stderr = "/dev/tty5") + + # Calling abootconf to configure the installed aboot. The + # first argument is the disk to use, the second argument is + # the number of the partition on which aboot.conf resides. + # It's always the boot partition whether it's / or /boot (with + # the mount point being omitted.) + args = ("abootconf", ("/dev/%s" % wbd), str (bdpn)) + #log("abootconf command: %s" %(args,)) + rhpl.executil.execWithRedirect ('/sbin/abootconf', args, + root = instRoot, + stdout = "/dev/tty5", + stderr = "/dev/tty5") + + + def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfig, intf): + if len(kernelList) < 1: + self.noKernelsWarn(intf) + + if self.useMilo(): + intf.messageWindow(_("MILO Not Supported"), + "This system requires the support of MILO to " + + "boot linux (MILO is not included in this " + + "distribution.) The installation of the " + + "bootloader can't be completed on this system.") + # elif not justConfig FIXME. + else: + self.writeAboot(instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev, justConfig) + + def __init__(self): + bootloaderInfo.__init__(self) + self.useGrubVal = 0 + self.configfile = "/etc/aboot.conf" + # self.kernelLocation is already set to what we need. + self.password = None + self.pure = None + + +class ppcBootloaderInfo(bootloaderInfo): + def getBootDevs(self, fs, bl): + import fsset + + devs = [] + machine = rhpl.getPPCMachine() + + if machine == 'pSeries': + for entry in fs.entries: + if isinstance(entry.fsystem, fsset.prepbootFileSystem) \ + and entry.format: + devs.append('/dev/%s' % (entry.device.getDevice(),)) + elif machine == 'PMac': + for entry in fs.entries: + if isinstance(entry.fsystem, fsset.applebootstrapFileSystem) \ + and entry.format: + devs.append('/dev/%s' % (entry.device.getDevice(),)) + + if len(devs) == 0: + # Try to get a boot device; bplan OF understands ext3 + if machine == 'Pegasos' or machine == 'Efika': + entry = fs.getEntryByMountPoint('/boot') + # Try / if we don't have this we're not going to work + if not entry: + entry = fs.getEntryByMountPoint('/') + if entry: + dev = "/dev/%s" % (entry.device.getDevice(asBoot=1),) + devs.append(dev) + else: + if bl.getDevice(): + devs.append("/dev/%s" % bl.getDevice()) + return devs + + + def writeYaboot(self, instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev, justConfigFile): + + from flags import flags + + yabootTarget = string.join(self.getBootDevs(fsset, bl)) + + bootDev = fsset.getEntryByMountPoint("/boot") + if bootDev: + cf = "/boot/etc/yaboot.conf" + cfPath = "" + if not os.path.isdir(instRoot + "/boot/etc"): + os.mkdir(instRoot + "/boot/etc") + else: + bootDev = fsset.getEntryByMountPoint("/") + cfPath = "/boot" + cf = "/etc/yaboot.conf" + bootDev = bootDev.device.getDevice(asBoot = 1) + + f = open(instRoot + cf, "w+") + + f.write("# yaboot.conf generated by anaconda\n\n") + + f.write("boot=%s\n" %(yabootTarget,)) + f.write("init-message=\"Welcome to %s!\\nHit for boot options\"\n\n" + %(getProductName(),)) + + (name, partNum) = getDiskPart(bootDev) + partno = partNum + 1 # 1 based + + f.write("partition=%s\n" %(partno,)) + + f.write("timeout=%s\n" % (self.timeout or 80)) + f.write("install=/usr/lib/yaboot/yaboot\n") + f.write("delay=5\n") + f.write("enablecdboot\n") + f.write("enableofboot\n") + f.write("enablenetboot\n") + + yabootProg = "/sbin/mkofboot" + if rhpl.getPPCMachine() == "PMac": + # write out the first hfs/hfs+ partition as being macosx + for (label, longlabel, device) in chainList: + if ((not label) or (label == "")): + continue + f.write("macosx=/dev/%s\n" %(device,)) + break + + f.write("magicboot=/usr/lib/yaboot/ofboot\n") + + elif rhpl.getPPCMachine() == "pSeries": + f.write("nonvram\n") + f.write("fstype=raw\n") + + else: # Default non-destructive case for anything else. + f.write("nonvram\n") + f.write("mntpoint=/boot/yaboot\n") + f.write("usemount\n") + if not os.access(instRoot + "/boot/yaboot", os.R_OK): + os.mkdir(instRoot + "/boot/yaboot") + yabootProg = "/sbin/ybin" + + if self.password: + f.write("password=%s\n" %(self.password,)) + f.write("restricted\n") + + f.write("\n") + + rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + + for (label, longlabel, version) in kernelList: + kernelTag = "-" + version + kernelFile = "%s/vmlinuz%s" %(cfPath, kernelTag) + + f.write("image=%s\n" %(kernelFile,)) + f.write("\tlabel=%s\n" %(label,)) + f.write("\tread-only\n") + + initrd = booty.makeInitrd(kernelTag, instRoot) + if os.access(instRoot + initrd, os.R_OK): + f.write("\tinitrd=%s/initrd%s.img\n" %(cfPath,kernelTag)) + + append = "%s" %(self.args.get(),) + + realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + if rootIsDevice(realroot): + f.write("\troot=%s\n" %(realroot,)) + else: + if len(append) > 0: + append = "%s root=%s" %(append,realroot) + else: + append = "root=%s" %(realroot,) + + if len(append) > 0: + f.write("\tappend=\"%s\"\n" %(append,)) + f.write("\n") + + f.close() + os.chmod(instRoot + cf, 0600) + + # FIXME: hack to make sure things are written to disk + import isys + isys.sync() + isys.sync() + isys.sync() + + ybinargs = [ yabootProg, "-f", "-C", cf ] + + #log("running: %s" %(ybinargs,)) + if not flags.test: + rhpl.executil.execWithRedirect(ybinargs[0], + ybinargs, + stdout = "/dev/tty5", + stderr = "/dev/tty5", + root = instRoot) + + if (not os.access(instRoot + "/etc/yaboot.conf", os.R_OK) and + os.access(instRoot + "/boot/etc/yaboot.conf", os.R_OK)): + os.symlink("../boot/etc/yaboot.conf", + instRoot + "/etc/yaboot.conf") + + return "" + + def setPassword(self, val, isCrypted = 1): + # yaboot just handles the password and doesn't care if its crypted + # or not + self.password = val + + def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfig, intf): + if len(kernelList) >= 1: + out = self.writeYaboot(instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev, justConfig) + else: + self.noKernelsWarn(intf) + + def __init__(self): + bootloaderInfo.__init__(self) + self.useYabootVal = 1 + self.kernelLocation = "/boot" + self.configfile = "/etc/yaboot.conf" + + +class iseriesBootloaderInfo(bootloaderInfo): + def ddFile(self, inf, of, bs = 4096): + src = os.open(inf, os.O_RDONLY) + dest = os.open(of, os.O_WRONLY | os.O_CREAT) + size = 0 + + buf = os.read(src, bs) + while len(buf) > 0: + size = size + len(buf) + os.write(dest, buf) + buf = os.read(src, bs) + + os.close(src) + os.close(dest) + + return size + + def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfig, intf): + if len(kernelList) < 1: + self.noKernelsWarn(intf) + return + #if len(kernelList) > 1: + # # FIXME: how can this happen? + # log("more than one kernel on iSeries. bailing and just using " + # "the first") + + # iseries is Weird (tm) -- here's the basic theory + # a) have /boot/vmlinitrd-$(version) + # b) copy default kernel to PReP partition + # c) dd default kernel to /proc/iSeries/mf/C/vmlinux + # d) set cmdline in /boot/cmdline-$(version) + # e) copy cmdline to /proc/iSeries/mf/C/cmdline + # f) set default side to 'C' i /proc/iSeries/mf/side + # g) put default kernel and cmdline on side B too (#91038) + + rootDevice = fsset.getEntryByMountPoint("/").device.getDevice() + + # write our command line files + for (kernel, tag, kernelTag) in kernelList: + cmdFile = "%scmdline-%s" %(self.kernelLocation, kernelTag) + initrd = "%sinitrd-%s.img" %(self.kernelLocation, kernelTag) + realroot = getRootDevName(initrd, fsset, rootDevice, instRoot) + f = open(instRoot + cmdFile, "w") + f.write("ro root=%s" %(realroot,)) + if bl.args.get(): + f.write(" %s" %(bl.args.get(),)) + f.write("\n") + f.close() + os.chmod(instRoot + cmdFile, 0644) + + kernel, tag, kernelTag = kernelList[0] + kernelFile = "%svmlinitrd-%s" %(self.kernelLocation, kernelTag) + + # write the kernel to the PReP partition since that's what + # OS/400 will load as NWSSTG + bootDev = bl.getDevice() + if bootDev: + #log("Writing kernel %s to PReP partition %s" %(kernelFile, bootDev)) + try: + self.ddFile(instRoot + kernelFile, "%s/dev/%s" %(instRoot, + bootDev)) + except Exception, e: + # FIXME: should this be more fatal + #log("Failed to write kernel: %s" %(e,)) + pass + else: + #log("No PReP boot partition, not writing kernel for NWSSTG") + pass + + + # now, it's a lot faster to boot if we don't make people go back + # into OS/400, so set up side C (used by default for NWSSTG) with + # our current bits + for side in ("C", "B"): + #log("Writing kernel and cmdline to side %s" %(side,)) + wrotekernel = 0 + try: + self.ddFile(instRoot + kernelFile, + "%s/proc/iSeries/mf/%s/vmlinux" %(instRoot, side)) + wrotekernel = 1 + except Exception, e: + # FIXME: should this be more fatal? + #log("Failed to write kernel to side %s: %s" %(side, e)) + pass + + if wrotekernel == 1: + try: + # blank it. ugh. + f = open("%s/proc/iSeries/mf/%s/cmdline" %(instRoot, side), + "w+") + f.write(" " * 255) + f.close() + + self.ddFile("%s/%scmdline-%s" %(instRoot, + self.kernelLocation, + kernelTag), + "%s/proc/iSeries/mf/%s/cmdline" %(instRoot, + side)) + except Exception, e: + #log("Failed to write kernel command line to side %s: %s" + # %(side, e)) + pass + + #log("Setting default side to C") + f = open(instRoot + "/proc/iSeries/mf/side", "w") + f.write("C") + f.close() + + def __init__(self): + bootloaderInfo.__init__(self) + self.kernelLocation = "/boot/" + +class isolinuxBootloaderInfo(bootloaderInfo): + def __init__(self): + bootloaderInfo.__init__(self) + self.kernelLocation = "/boot" + self.configfile = "/boot/isolinux/isolinux.cfg" + + def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfig, intf = None): + if not os.path.isdir(instRoot + "/boot/isolinux"): + os.mkdir(instRoot + "/boot/isolinux") + + f = open(instRoot + "/boot/isolinux/isolinux.cfg", "w+") + f.write("# isolinux.cfg generated by anaconda\n\n") + + f.write("prompt 1\n") + f.write("timeout %s\n" % (self.timeout or 600)) + + # FIXME: as this stands, we can really only handle one due to + # filename length limitations with base iso9660. fun, fun. + for (label, longlabel, version) in kernelList: + # XXX hackity, hack hack hack. but we need them in a different + # path for live cd only + shutil.copy("%s/boot/vmlinuz-%s" %(instRoot, version), + "%s/boot/isolinux/vmlinuz" %(instRoot,)) + shutil.copy("%s/boot/initrd-%s.img" %(instRoot, version), + "%s/boot/isolinux/initrd.img" %(instRoot,)) + + # FIXME: need to dtrt for xen kernels with multiboot com32 module + f.write("label linux\n") + f.write("\tkernel vmlinuz\n") + f.write("\tappend initrd=initrd.img,initlive.gz\n") + f.write("\n") + + break + + f.close() + os.chmod(instRoot + "/boot/isolinux/isolinux.cfg", 0600) + + # copy the isolinux bin + shutil.copy(instRoot + "/usr/lib/syslinux/isolinux-debug.bin", + instRoot + "/boot/isolinux/isolinux.bin") + + +class sparcBootloaderInfo(bootloaderInfo): + def writeSilo(self, instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev, justConfigFile): + + from flags import flags + + bootDev = fsset.getEntryByMountPoint("/boot") + mf = '/silo.message' + if bootDev: + cf = "/boot/silo.conf" + mfdir = '/boot' + cfPath = "" + if not os.path.isdir(instRoot + "/boot"): + os.mkdir(instRoot + "/boot") + else: + bootDev = fsset.getEntryByMountPoint("/") + cf = "/etc/silo.conf" + mfdir = '/etc' + cfPath = "/boot" + bootDev = bootDev.device.getDevice(asBoot = 1) + + f = open(instRoot + mfdir + mf, "w+") + f.write("Welcome to %s!\nHit for boot options\n\n" % \ + (getProductName(),)) + f.close() + os.chmod(instRoot + mfdir + mf, 0600) + + f = open(instRoot + cf, "w+") + f.write("# silo.conf generated by anaconda\n\n") + + f.write("#boot=%s\n" % (bootDev,)) + f.write("message=%s\n" % (mf,)) + f.write("timeout=%s\n" % (self.timeout or 50)) + + (name, partNum) = getDiskPart(bootDev) + partno = partNum + 1 + f.write("partition=%s\n" % (partno,)) + + if self.password: + f.write("password=%s\n" % (self.password,)) + f.write("restricted\n") + + f.write("default=%s\n" % (kernelList[0][0],)) + f.write("\n") + + rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + + for (label, longlabel, version) in kernelList: + kernelTag = "-" + version + kernelFile = "%s/vmlinuz%s" % (cfPath, kernelTag) + + f.write("image=%s\n" % (kernelFile,)) + f.write("\tlabel=%s\n" % (label,)) + f.write("\tread-only\n") + + initrd = booty.makeInitrd(kernelTag, instRoot) + if os.access(instRoot + initrd, os.R_OK): + f.write("\tinitrd=%s/initrd%s.img\n" % (cfPath, kernelTag)) + + append = "%s" % (self.args.get(),) + + realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + if rootIsDevice(realroot): + f.write("\troot=%s\n" % (realroot,)) + else: + if len(append) > 0: + append = "%s root=%s" % (append, realroot) + else: + append = "root=%s" % (realroot,) + + if len(append) > 0: + f.write("\tappend=\"%s\"\n" % (append,)) + f.write("\n") + + f.close() + os.chmod(instRoot + cf, 0600) + + # FIXME: hack to make sure things are written to disk + import isys + isys.sync() + isys.sync() + isys.sync() + + backup = "%s/backup.b" % (cfPath,) + sbinargs = ["/sbin/silo", "-f", "-C", cf, "-S", backup] + # TODO!!! FIXME!!! XXX!!! + # butil is not defined!!! - assume this is in rhpl now? + if butil.getSparcMachine() == "sun4u": + sbinargs += ["-u"] + else: + sbinargs += ["-U"] + + #log("running: %s" % (sbinargs,)) + if not flags.test: + rhpl.executil.execWithRedirect(sbinargs[0], + sbinargs, + stdout = "/dev/tty5", + stderr = "/dev/tty5", + root = instRoot) + + if (not os.access(instRoot + "/etc/silo.conf", os.R_OK) and + os.access(instRoot + "/boot/etc/silo.conf", os.R_OK)): + os.symlink("../boot/etc/silo.conf", + instRoot + "/etc/silo.conf") + + return "" + + def setPassword(self, val, isCrypted = 1): + # silo just handles the password unencrypted + self.password = val + + def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfig, intf): + if len(kernelList) >= 1: + self.writeSilo(instRoot, fsset, bl, langs, kernelList, chainList, + defaultDev, justConfig) + else: + self.noKernelsWarn(intf) + + def __init__(self): + bootloaderInfo.__init__(self) + self.useSiloVal = 1 + self.kernelLocation = "/boot" + self._configdir = "/etc" + self._configname = "silo.conf" + +############### +# end of boot loader objects... these are just some utility functions used + +# return (disk, partition number) eg ('hda', 1) +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/')): + if dev[-2] == 'p': + cut = -1 + elif dev[-3] == 'p': + cut = -2 + else: + if dev[-2] in string.digits: + cut = -2 + elif dev[-1] in string.digits: + cut = -1 + + name = dev[:cut] + + # hack off the trailing 'p' from /dev/cciss/*, for example + if name[-1] == 'p': + for letter in name: + if letter not in string.letters and letter != "/": + name = name[:-1] + break + + if cut < 0: + partNum = int(dev[cut:]) - 1 + else: + partNum = None + + return (name, partNum) + +def rootIsDevice(dev): + if dev.startswith("LABEL=") or dev.startswith("UUID="): + return False + return True + +# hackery to determine if we should do root=LABEL=/ or whatnot +# as usual, knows too much about anaconda +def getRootDevName(initrd, fsset, rootDev, instRoot): + if not os.access(instRoot + initrd, os.R_OK): + return "/dev/%s" % (rootDev,) + + try: + rootEntry = fsset.getEntryByMountPoint("/") + if rootEntry.getUuid() is not None: + return "UUID=%s" %(rootEntry.getUuid(),) + elif rootEntry.getLabel() is not None and rootEntry.device.doLabel is not None: + return "LABEL=%s" %(rootEntry.getLabel(),) + return "/dev/%s" %(rootDev,) + except: + return "/dev/%s" %(rootDev,) + +# returns a product name to use for the boot loader string +# FIXME: this is based on the stuff from anaconda, but kind of crappy :-/ +def getProductName(): + # try redhat-release first + if os.access("/etc/redhat-release", os.R_OK): + f = open("/etc/redhat-release", "r") + lines = f.readlines() + f.close() + for buf in lines: + relidx = buf.find(" release") + if relidx != -1: + return buf[:relidx] + + if os.access("/tmp/product/.buildstamp", os.R_OK): + path = "/tmp/product/.buildstamp" + elif os.access("/.buildstamp", os.R_OK): + path = "/.buildstamp" + else: + path = None + + if path is not None: + f = open(path, "r") + lines = f.readlines() + f.close() + if len(lines) >= 2: + return lines[1][:-1] + + # fall back + return "Red Hat Linux" + + diff --git a/booty/booty.py b/booty/booty.py new file mode 100644 index 000000000..b8045bc45 --- /dev/null +++ b/booty/booty.py @@ -0,0 +1,385 @@ +# +# bootloader.py - generic boot loader handling backend for up2date and anaconda +# +# Jeremy Katz +# Adrian Likins +# Peter Jones +# +# Copyright 2001-2005 Red Hat, Inc. +# +# This software may be freely redistributed under the terms of the GNU +# library public license. +# +# 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. +# +"""Module for manipulation and creation of boot loader configurations""" + +import os, sys +import stat +import time +import shutil +import lilo + +import checkbootloader +import rhpl +import rhpl.executil +from bootloaderInfo import * + + +# return instance of the appropriate bootloader for our arch +def getBootloader(): + """Get the bootloader info object for your architecture""" + if rhpl.getArch() == 'i386': + return x86BootloaderInfo() + elif rhpl.getArch() == 'ia64': + return ia64BootloaderInfo() + elif rhpl.getArch() == 's390' or rhpl.getArch() == "s390x": + return s390BootloaderInfo() + elif rhpl.getArch() == "alpha": + return alphaBootloaderInfo() + elif rhpl.getArch() == "x86_64": + return x86BootloaderInfo() + elif rhpl.getPPCMachine() == "iSeries": + return iseriesBootloaderInfo() + elif rhpl.getArch() == "ppc": + return ppcBootloaderInfo() + elif rhpl.getArch() == "sparc": + return sparcBootloaderInfo() + else: + return bootloaderInfo() + + +# install the kernels in kernelList to your bootloader's config file +# (if needed) +# kernelList is a python list of (kernelVersion, extraInfo) tuples +# backupSuffix is the suffix to use backing up the config file if +# we change it +# test is whether or not this is test mode +# filename is a filename to use instead of the default (testing only) +def installNewKernelImages(kernelList, backupSuffix = "rpmsave", test = 0, + filename = None): + """Add the kernels in kernelList to the current boot loader config file""" + + if rhpl.getArch() == 'i386': + return __installNewKernelImagesX86(kernelList, backupSuffix, test, + filename) + elif rhpl.getArch() == 'ia64': + return __installNewKernelImagesIA64(kernelList, backupSuffix, test, + filename) + elif rhpl.getArch() == 'ppc': + return __installNewKernelImagesPPC(kernelList, backupSuffix, test, + filename) + elif rhpl.getArch() == 'sparc': + return __installNewKernelImagesSparc(kernelList, backupSuffix, test, + filename) + else: + raise RuntimeError, "Don't know how to add new kernels for %s" % \ + (rhpl.getArch(),) + + +def __installNewKernelImagesX86(kernelList, backupSuffix, test, filename): + theBootloader = checkbootloader.whichBootLoader() + if theBootloader == "GRUB": + __installNewKernelImagesX86Grub(kernelList, backupSuffix, test) + else: + raise RuntimeError, "Cannot determine x86 bootloader in use." + +def __installNewKernelImagesX86Grub(kernelList, backupSuffix, test): + # get the current default kernel in the grub config + def getGrubDefault(): + pipe = os.popen("/sbin/grubby --default-kernel") + ret = pipe.read() + ret = string.strip(ret) + + return ret + + # set the default kernel in grub to the one at path + def setGrubDefault(path, instRoot="/"): + args = [instRoot + "/sbin/grubby", "--set-default", path] + ret = rhpl.executil.execWithRedirect(args[0], args, + stdout = None, stderr = None) + + return ret + + defaultImage = getGrubDefault() + if test: + print "defaultImage is %s" % (defaultImage,) + + # if we don't get any sort of answer back, do nothing + if defaultImage: + defaultType = getDefaultKernelType(defaultImage) + + # look for a kernel image of the same type + for (newVersion, imageType) in kernelList: + if defaultType == imageType: + if test: + print "Would have set default to /boot/vmlinuz-%s" % (newVersion,) + else: + setGrubDefault("/boot/vmlinuz-%s" % (newVersion,)) + + + +def __installNewKernelImagesIA64(kernelList, backupSuffix, test, filename): + if not filename: + filename = "/boot/efi/elilo.conf" + + config = updateLiloishConfigFile(kernelList, "/boot/efi/vmlinuz-%s", + test, filename) + + backup = writeConfig(config, filename, backupSuffix, test) + +def __installNewKernelImagesPPC(kernelList, backupSuffix, test, filename): + if not filename: + filename = "/etc/yaboot.conf" + + config = updateLiloishConfigFile(kernelList, "/boot/vmlinu-%s", + test, filename) + + backup = writeConfig(config, filename, backupSuffix, test) + + ret = yabootInstall("/") + if ret: + restoreBackup(filename, backup) + raise RuntimeError, "Real install of yaboot failed" + +def __installNewKernelImagesSparc(kernelList, backupSuffix, test, filename): + if not filename: + filename = "/etc/silo.conf" + + config = updateLiloishConfigFile(kernelList, "/boot/vmlinuz-%s", + test, filename) + + backup = writeConfig(config, filename, backupSuffix, test) + + ret = siloInstall("/") + if ret: + restoreBackup(filename, backup) + raise RuntimeError, "Real install of silo failed" + +# used for updating lilo-ish config files (eg elilo as well) +def updateLiloishConfigFile(kernelList, kernelPathFormat, test, configFile): + config = lilo.LiloConfigFile() + + # XXX should be able to create if one doesn't exist + if not os.access(configFile, os.R_OK): + return None + + # read in the config file and make sure we don't have any unsupported + # options + config.read(configFile) + if len(config.unsupported): + raise RuntimeError, ("Unsupported options in config file: %s" % + (config.unsupported,)) + + default = config.getDefaultLinux() + if not default: + raise RuntimeError, "Unable to find default linux entry" + + realDefault = config.getDefault() + setdefault = None + + defaultType = getDefaultKernelType(default.path) + rootDev = default.getEntry("root") + + for (newVersion, imageType) in kernelList: + path = kernelPathFormat % (newVersion,) + + initrd = makeInitrd("-%s" % (newVersion,), "/") + + if not os.access(initrd, os.R_OK): + initrd = None + + if imageType and imageType != defaultType: + # linux-smp.linux-BOOT, etc + label = "linux-"+imageType + elif not imageType and defaultType: + label = "linux-up" + else: + label = "linux" + + if label in config.listImages(): + backup = backupLabel(label, config.listImages()) + oldImage = config.getImage(label)[1] + oldImage.addEntry("label", backup) + if test: + print "Renamed %s to %s" % (label, backup) + + # alikins did this once, I'm not doing it again - katzj + if defaultType: + if imageType: + if defaultType == imageType: + setdefault = label + else: + if not imageType: + setdefault = label + + addImage(path, initrd, label, config, default) + + # if the default linux image's label is the same as the real default's + # label, then set what we've figured out as the default to be the + # default + if (default.getEntry('label') == realDefault.getEntry('label')) \ + and setdefault: + config.addEntry("default", setdefault) + else: # make sure the default entry is explicitly set + config.addEntry("default", realDefault.getEntry('label')) + + if test: + print config + return config + + +# path is the path to the new kernel image +# initrd is the path to the initrd (or None if it doesn't exist) +# label is the label for the image +# config is the full LiloConfigFile object for the config file +# default is the LiloConfigFile object for the default Linux-y entry +def addImage(path, initrd, label, config, default): + # these directives must be on a per-image basis and are non-sensical + # otherwise + dontCopy = ['initrd', 'alias', 'label'] + + entry = lilo.LiloConfigFile(imageType = "image", path = path) + + if label: + entry.addEntry("label", label) + else: + raise RuntimeError, "Unable to determine a label for %s. Aborting" % (path,) + + if initrd: + entry.addEntry("initrd", initrd) + + # go through all of the things listed for the default + # config entry and if they're not in our blacklist + # of stuff not to copy, go ahead and copy it + entries = default.listEntries() + for key in entries.keys(): + if key in dontCopy: + pass + else: + entry.addEntry(key, default.getEntry(key)) + + config.addImage(entry, 1) + + +# note that this function no longer actually creates an initrd. +# the kernel's %post does this now +def makeInitrd (kernelTag, instRoot): + if rhpl.getArch() == 'ia64': + initrd = "/boot/efi/EFI/redhat/initrd%s.img" % (kernelTag, ) + else: + initrd = "/boot/initrd%s.img" % (kernelTag, ) + + return initrd + + +# determine the type of the default kernel +# kernelPath is the path of the current default +def getDefaultKernelType(kernelPath): + # XXX this isn't an ideal way to do this. up2date was poking at the + # rpmdb. this is a simple but stupid way to figure out the simple + # cases for now + defaultType = None + if kernelPath[-3:] == "smp": + defaultType = "smp" + elif kernelPath[-10:] == "enterprise": + defaultType = "enterprise" + elif kernelPath[-4:] == "BOOT": + defaultType = "BOOT" + elif kernelPath[-3:] == "ans": + defaultType = "ans" + elif kernelPath[-4:] == "briq": + defaultType = "briq" + elif kernelPath[-5:] == "teron": + defaultType = "teron" + elif kernelPath[-7:] == "pseries": + defaultType = "pseries" + elif kernelPath[-7:] == "iseries": + defaultType = "iseries" + else: + defaultType = None + + return defaultType + + +# make a silly .bak entry +def backupLabel(label, imageList): + backup = "%s.bak" % (label,) + while backup in imageList: + backup = backup + "_" + + # truncate long names. + if len(backup) > 32: + backup = backup[:28]+".bak" + + if backup in imageList: + raise RuntimeError, "Attempts to create unique backup label for %s failed" % (label,) + + return backup + + +def writeConfig(config, filename, backupSuffix, test = 0): + # write out the LILO config + try: + backup = backupFile(filename, backupSuffix) + liloperms = stat.S_IMODE(os.stat(filename)[stat.ST_MODE]) + if test: + filename = "/tmp/lilo.conf" + + config.write(filename, perms = liloperms) + except: + restoreBackup(filename, backup) + raise RuntimeError, "Error installing updated config file. Aborting" + + return backup + + +def backupFile(filename, suffix): + backup = "%s.%s-%s" % (filename, suffix, repr(time.time())) + + # make sure the backup file doesn't exist... add _'s until it doesn't + while os.access(backup, os.F_OK): + backup = backup + "_" + + shutil.copy(filename, backup) + + return backup + + +def restoreBackup(filename, backup): + shutil.copy(backup, filename) + os.remove(backup) + + +def liloInstall(instRoot, test = 0, filename = None, testFlag = 0): + args = [ instRoot + "/sbin/lilo", "-r", instRoot ] + + if test: + args.extend(["-t"]) + + if testFlag: + print args + ret = 0 + else: + ret = rhpl.executil.execWithRedirect(args[0], args, + stdout = None, stderr = None) + + return ret + +def yabootInstall(instRoot, filename = None): + args = [ instRoot + "/usr/sbin/ybin", "-r", instRoot ] + + ret = rhpl.executil.execWithRedirect(args[0], args, + stdout = None, stderr = None) + + return ret + +def siloInstall(instRoot, filename = None): + args = [instRoot + "/sbin/silo", "-r", instRoot] + + ret = rhpl.executil.execWithRedirect(args[0], args, + stdout = None, stderr = None) + + return ret diff --git a/booty/checkbootloader.py b/booty/checkbootloader.py new file mode 100644 index 000000000..38cb8255d --- /dev/null +++ b/booty/checkbootloader.py @@ -0,0 +1,270 @@ +#!/usr/bin/python +# +# Check to see whether it looks like GRUB or LILO is the boot loader +# being used on the system. +# +# Jeremy Katz +# Peter Jones +# +# Copyright 2001,2005 Red Hat, Inc. +# +# This software may be freely redistributed under the terms of the GNU +# library public license. +# +# 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. + +import os +import string +import rhpl + +grubConfigFile = "/etc/grub.conf" +liloConfigFile = "/etc/lilo.conf" +yabootConfigFile = "/etc/yaboot.conf" +siloConfigFile = "/etc/silo.conf" + + +# XXX: this is cut and pasted directly from booty/bootloaderInfo.py +# should eventually just go from there +def getDiskPart(dev): + """Return (disk, partition number) tuple for dev""" + cut = len(dev) + if (dev[:3] == "rd/" or dev[:4] == "ida/" or + dev[:6] == "cciss/"): + if dev[-2] == 'p': + cut = -1 + elif dev[-3] == 'p': + cut = -2 + else: + if dev[-2] in string.digits: + cut = -2 + elif dev[-1] in string.digits: + cut = -1 + + name = dev[:cut] + + # hack off the trailing 'p' from /dev/cciss/*, for example + if name[-1] == 'p': + for letter in name: + if letter not in string.letters and letter != "/": + name = name[:-1] + break + + if cut < 0: + partNum = int(dev[cut:]) - 1 + else: + partNum = None + + return (name, partNum) + + +def getRaidDisks(raidDevice, raidLevel=None, stripPart=1): + rc = [] + if raidLevel is not None: + try: + raidLevel = "raid%d" % (int(raidLevel),) + except ValueError: + pass + + try: + f = open("/proc/mdstat", "r") + lines = f.readlines() + f.close() + except: + return rc + + for line in lines: + fields = string.split(line, ' ') + if fields[0] == raidDevice: + if raidLevel is not None and fields[3] != raidLevel: + continue + for field in fields[4:]: + if string.find(field, "[") == -1: + continue + dev = string.split(field, '[')[0] + if len(dev) == 0: + continue + if stripPart: + disk = getDiskPart(dev)[0] + rc.append(disk) + else: + rc.append(dev) + + return rc + + +def getBootBlock(bootDev, instRoot, seekBlocks=0): + """Get the boot block from bootDev. Return a 512 byte string.""" + block = " " * 512 + if bootDev is None: + return block + + # get the devices in the raid device + if bootDev[5:7] == "md": + bootDevs = getRaidDisks(bootDev[5:]) + bootDevs.sort() + else: + bootDevs = [ bootDev[5:] ] + + # FIXME: this is kind of a hack + # look at all of the devs in the raid device until we can read the + # boot block for one of them. should do this better at some point + # by looking at all of the drives properly + for dev in bootDevs: + try: + fd = os.open("%s/dev/%s" % (instRoot, dev), os.O_RDONLY) + if seekBlocks > 0: + os.lseek(fd, seekBlocks * 512, 0) + block = os.read(fd, 512) + os.close(fd) + return block + except: + pass + return block + +# takes a line like #boot=/dev/hda and returns /dev/hda +# also handles cases like quoted versions and other nonsense +def getBootDevString(line): + dev = string.split(line, '=')[1] + dev = string.strip(dev) + dev = string.replace(dev, '"', '') + dev = string.replace(dev, "'", "") + return dev + +def getBootDevList(line): + devs = string.split(line, '=')[1] + rets = [] + for dev in devs: + dev = getBootDevString("=%s" % (dev,)) + rets.append(dev) + return string.join(rets) + +efi = None +## Determine if the hardware supports EFI. +# @return True if so, False otherwise. +def isEfi(): + global efi + if efi is not None: + return efi + + efi = False + if rhpl.getArch() in ("ia64", "i386", "x86_64"): + # XXX need to make sure efivars is loaded... + if os.path.exists("/sys/firmware/efi"): + efi = True + + return efi + +def getBootloaderTypeAndBoot(instRoot = "/"): + haveGrubConf = 1 + haveLiloConf = 1 + haveYabootConf = 1 + haveSiloConf = 1 + + bootDev = None + + # make sure they have the config file, otherwise we definitely can't + # use that bootloader + if not os.access(instRoot + grubConfigFile, os.R_OK): + haveGrubConf = 0 + if not os.access(instRoot + liloConfigFile, os.R_OK): + haveLiloConf = 0 + if not os.access(instRoot + yabootConfigFile, os.R_OK): + haveYabootConf = 0 + if not os.access(instRoot + siloConfigFile, os.R_OK): + haveSiloConf = 0 + + if haveGrubConf: + bootDev = None + for (fn, stanza) in [ ("/etc/sysconfig/grub", "boot="), + (grubConfigFile, "#boot=") ]: + try: + f = open(instRoot + fn, "r") + except: + continue + + # the following bits of code are straight from checkbootloader.py + lines = f.readlines() + f.close() + for line in lines: + if line.startswith(stanza): + bootDev = getBootDevString(line) + break + if bootDev is not None: + break + + if isEfi(): + return ("GRUB", bootDev) + + if bootDev is not None: + block = getBootBlock(bootDev, instRoot) + # XXX I don't like this, but it's what the maintainer suggested :( + if string.find(block, "GRUB") >= 0: + return ("GRUB", bootDev) + + if haveLiloConf: + f = open(instRoot + liloConfigFile, "r") + lines = f.readlines() + for line in lines: + if line[0:5] == "boot=": + bootDev = getBootDevString(line) + break + + block = getBootBlock(bootDev, instRoot) + # this at least is well-defined + if block[6:10] == "LILO": + return ("LILO", bootDev) + + if haveYabootConf: + f = open(instRoot + yabootConfigFile, "r") + lines = f.readlines() + for line in lines: + if line[0:5] == "boot=": + bootDev = getBootDevList(line) + + if bootDev: + return ("YABOOT", bootDev) + + if haveSiloConf: + bootDev = None + # We've never done the /etc/sysconfig/silo thing, but maybe + # we should start... + for (fn, stanza) in [ ("/etc/sysconfig/silo", "boot="), + (grubConfigFile, "#boot=") ]: + try: + f = open(instRoot + fn, "r") + except: + continue + + lines = f.readlines() + f.close() + for line in lines: + if line.startswith(stanza): + bootDev = getBootDevString(line) + break + if bootDev is not None: + break + + if bootDev is not None: + # XXX SILO sucks just like grub. + if getDiskPart(bootDev)[1] != 3: + block = getBootBlock(bootDev, instRoot, 1) + if block[24:28] == "SILO": + return ("SILO", bootDev) + + return (None, None) + +def whichBootLoader(instRoot = "/"): + ret = getBootloaderTypeAndBoot(instRoot) + if not ret: + return None + else: + return ret[0] + +if __name__ == "__main__": + bootloader = whichBootLoader() + if bootloader: + print "Found %s." % (bootloader) + else: + print "Unable to determine boot loader." diff --git a/booty/lilo.py b/booty/lilo.py new file mode 100644 index 000000000..5f667e557 --- /dev/null +++ b/booty/lilo.py @@ -0,0 +1,326 @@ +#!/usr/bin/python +# +# Module for manipulation of lilo.conf files. Original found +# in the anaconda installer +# Copyright (c) 1999-2001 Red Hat, Inc. Distributed under GPL. +# +# Author: Matt Wilson +# Eric Troan +# Adrian Likins +"""Module for manipulation of lilo.conf files.""" +import string +import os + +from UserDict import UserDict + + +class UserDictCase(UserDict): + """A dictionary with case insensitive keys""" + def __init__(self, data = {}): + UserDict.__init__(self) + # if we are passed a dictionary transfer it over... + for k in data.keys(): + kl = string.lower(k) + self.data[kl] = data[k] + # some methods used to make the class work as a dictionary + def __setitem__(self, key, value): + key = string.lower(key) + self.data[key] = value + def __getitem__(self, key): + key = string.lower(key) + if not self.data.has_key(key): + return None + return self.data[key] + get = __getitem__ + def __delitem__(self, key): + key = string.lower(key) + del self.data[key] + def has_key(self, key): + key = string.lower(key) + return self.data.has_key(key) + # return this data as a real hash + def get_hash(self): + return self.data + # return the data for marshalling + def __getstate__(self): + return self.data + # we need a setstate because of the __getstate__ presence screws up deepcopy + def __setstate__(self, state): + self.__init__(state) + # get a dictionary out of this instance ({}.update doesn't get instances) + def dict(self): + return self.data + + +def needsEnterpriseKernel(): + rc = 0 + + try: + f = open("/proc/e820info", "r") + except IOError: + return 0 + for l in f.readlines(): + l = string.split(l) + if l[3] == '(reserved)': continue + + regionEnd = (string.atol(l[0], 16) - 1) + string.atol(l[2], 16) + if regionEnd > 0xffffffffL: + rc = 1 + + return rc + +class LiloConfigFile: + """class representing a lilo.conf lilo configuration file. Used to + manipulate the file directly""" + + def __repr__ (self, tab = 0): + s = "" + for n in self.order: + if (tab): + s = s + '\t' + if n[0] == '#': + s = s + n[1:] + else: + s = s + n + if self.items[n]: + s = s + "=" + self.items[n] + s = s + '\n' + for count in range(len(self.diskRemaps)): + s = s + "disk = %s\n" % self.diskRemaps[count][1] + s = s + "\tbios = %s\n" % self.biosRemaps[count][1] + for cl in self.images: + s = s + "\n%s=%s\n" % (cl.imageType, cl.path) + s = s + cl.__repr__(1) + return s + + def addEntry(self, item, val = None, replace = 1): + if not self.items.has_key(item): + self.order.append(item) + elif not replace: + return + + if (val): + self.items[item] = str(val) + else: + self.items[item] = None + + def getEntry(self, item): + if self.items.has_key(item): + return self.items[item] + else: + return None + + def delEntry(self, item): + newOrder = [] + for i in self.order: + if item != i: newOrder.append(i) + self.order = newOrder + + del self.items[item] + + def listEntries(self): + foo = self.items + return foo + + def testEntry(self, item): + if self.items.has_key(item): + return 1 + else: + return 0 + + def getImage(self, label): + for config in self.images: + # sanity check + if label is None: + break + if config.getEntry('label'): + if string.lower(config.getEntry('label')) == string.lower(label): + return (config.imageType, config, config.path, config.other) + if config.getEntry('alias'): + if string.lower(config.getEntry('alias')) == string.lower(label): + return (config.imageType, config, config.path, config.other) + + + raise IndexError, "unknown image %s" % (label) + + def addImage (self, config,first=None): + # make sure the config has a valid label + config.getEntry('label') + if not config.path or not config.imageType: + raise ValueError, "subconfig missing path or image type" + + if first: + self.images = [config] + self.images + else: + self.images.append(config) + + def delImage (self, label): + for config in self.images: + # sanity check + if label is None: + break + if config.getEntry('label'): + if string.lower(config.getEntry('label')) == string.lower(label): + self.images.remove (config) + return + + raise IndexError, "unknown image %s" % (label,) + + def getDefault (self): + default = None + try: + default = self.getEntry("default") + except: + pass + + if not default: + default = self.listImages()[0] + + theDefault = self.getImage(default) + + return theDefault[1] + + def getDefaultLinux (self): + defaultIsOther = None + + # XXX ick... this code badly needs work =\ + theDefault = self.getDefault() + + if theDefault.other: + defaultIsOther = 1 + + # if the default is other, look for the first linux image + if theDefault.other: + for image_label in self.listImages(): + image = self.getImage(image_label)[1] + if not image.other: + theDefault = image + break + + # if we get here and are *still* an other, then we have no linux + # images. ick + if theDefault.other: + return None + else: + return theDefault + + def listImages (self): + l = [] + for config in self.images: + l.append(config.getEntry('label')) + return l + + def listAliases (self): + l = [] + for config in self.images: + if config.getEntry('alias'): + l.append(config.getEntry('alias')) + return l + + def getPath (self): + return self.path + + def write(self, file, perms = 0644): + f = open(file, "w") + f.write(self.__repr__()) + f.close() + os.chmod(file, perms) + + def read (self, file): + f = open(file, "r") + image = None + for l in f.readlines(): + l = l[:-1] + orig = l + while (l and (l[0] == ' ' or l[0] == '\t')): + l = l[1:] + if not l: + continue + if l[0] == '#' and not image: + self.order.append('#' + orig) + continue + fields = string.split(l, '=', 1) + if l[0] == '#' and image: + args = ('#' + l,) + elif (len(fields) == 2): + f0 = string.strip (fields [0]) + f1 = string.strip (fields [1]) + if (f0 != "append"): + # people are silly and put quotes brokenly in their + # lilo.conf but you have to use them for append. ARGH! + f1 = string.replace(f1, '"', '') + f1 = string.replace(f1, "'", "") + if (f0 == "image" or f0 == "other"): + if image: self.addImage(image) + image = LiloConfigFile(imageType = f0, + path = f1) + if (f0 == "other"): + image.other = 1 + args = None + else: + args = (f0, f1) + if (f0 == "disk"): + self.diskRemaps.append((f0,f1)) + args = None + if (f0 == "bios"): + self.biosRemaps.append((f0,f1)) + args = None + + else: + args = (string.strip (l),) + + if (args and image): + apply(image.addEntry, args) + elif args: + apply(self.addEntry, args) + + if image: self.addImage(image) + + f.close() + + def __init__(self, imageType = None, path = None): + self.imageType = imageType + self.path = path + self.order = [] + self.images = [] + self.other = None + self.items = UserDictCase() + self.biosRemaps = [] + self.diskRemaps = [] + self.unsupported = [] + + +if __name__ == "__main__": + import sys + #sys.path.append("") + config = LiloConfigFile () + config.read ('/etc/lilo.conf') + print config + print "image list", config.listImages() + config.delImage ('linux') + print '----------------------------------' + config = LiloConfigFile () + config.read ('/etc/lilo.conf') + print config + print '----------------------------------' + print '----------------------------------' + print "list images" + print config.listImages() + print config.getImage('linux') + print "----------------------------------" + print "addimage (testlinux)" + blip = """ +read-only +blippy-blob=sdfsdf +append=\"sdfasdfasdf\" +root=/dev/hda6 +""" + sl = LiloConfigFile(imageType = "image", path="/boot/somevmlinuz-2.4.0") + sl.addEntry("label", "newkernel") + sl.addEntry("initrd", "blipppy") + config.addImage(sl) + + print '-------------------------------------' + print "writing out /tmp/lilo.conf" + print config.write("/tmp/lilo.conf") + print config -- cgit From 165e4a1777862509ebd1785f789c1f3ec1ce496c Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 27 Feb 2009 16:19:51 -0500 Subject: Remove functions in booty that are duplicated in anaconda. --- booty/bootloaderInfo.py | 87 ++++++------------------------------------------ booty/checkbootloader.py | 56 +++---------------------------- 2 files changed, 15 insertions(+), 128 deletions(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 6d8add73a..36daa5637 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -30,6 +30,10 @@ import rhpl from rhpl.translate import _, N_ import rhpl.executil +from fsset import getDiskPart +import iutil +from product import * + import booty import checkbootloader @@ -201,8 +205,7 @@ class BootImages: self.default = entry.device.getDevice() (label, longlabel, type) = self.images[self.default] if not label: - self.images[self.default] = ("linux", - getProductName(), type) + self.images[self.default] = ("linux", productName, type) # XXX more internal anaconda knowledge def availableBootDevices(self, diskSet, fsset): @@ -1134,9 +1137,6 @@ class grubBootloaderInfo(bootloaderInfo): class efiBootloaderInfo(bootloaderInfo): - def isEfi(self): - return os.access("/sys/firmware/efi", os.R_OK) - def getBootloaderName(self): return self._bootloader bootloader = property(getBootloaderName, None, None, \ @@ -1160,7 +1160,7 @@ class efiBootloaderInfo(bootloaderInfo): fields = string.split(line) if len(fields) < 2: continue - if string.join(fields[1:], " ") == getProductName(): + if string.join(fields[1:], " ") == productName: entry = fields[0][4:8] rhpl.executil.execWithRedirect('/usr/sbin/efibootmgr', ["efibootmgr", "-b", entry, "-B"], @@ -1190,7 +1190,7 @@ class efiBootloaderInfo(bootloaderInfo): bootdisk = bootdisk[:-1] argv = [ "/usr/sbin/efibootmgr", "-c" , "-w", "-L", - getProductName(), "-d", "/dev/%s" % bootdisk, + productName, "-d", "/dev/%s" % bootdisk, "-p", bootpart, "-l", "\\EFI\\redhat\\" + self.bootloader ] rhpl.executil.execWithRedirect(argv[0], argv, root = instRoot, stdout = "/dev/tty5", @@ -1198,7 +1198,7 @@ class efiBootloaderInfo(bootloaderInfo): def installGrub(self, instRoot, bootDevs, grubTarget, grubPath, fsset, target, cfPath): - if not self.isEfi(): + if not iutil.isEfi(): raise EnvironmentError self.removeOldEfiEntries(instRoot) self.addNewEfiEntry(instRoot, fsset) @@ -1206,7 +1206,7 @@ class efiBootloaderInfo(bootloaderInfo): def __init__(self, initialize = True): if initialize: bootloaderInfo.__init__(self) - if self.isEfi(): + if iutil.isEfi(): self._configdir = "/boot/efi/EFI/redhat" self._configname = "grub.conf" self._bootloader = "grub.efi" @@ -1674,7 +1674,7 @@ class ppcBootloaderInfo(bootloaderInfo): f.write("boot=%s\n" %(yabootTarget,)) f.write("init-message=\"Welcome to %s!\\nHit for boot options\"\n\n" - %(getProductName(),)) + % productName) (name, partNum) = getDiskPart(bootDev) partno = partNum + 1 # 1 based @@ -1970,8 +1970,7 @@ class sparcBootloaderInfo(bootloaderInfo): bootDev = bootDev.device.getDevice(asBoot = 1) f = open(instRoot + mfdir + mf, "w+") - f.write("Welcome to %s!\nHit for boot options\n\n" % \ - (getProductName(),)) + f.write("Welcome to %s!\nHit for boot options\n\n" % productName) f.close() os.chmod(instRoot + mfdir + mf, 0600) @@ -2077,38 +2076,6 @@ class sparcBootloaderInfo(bootloaderInfo): ############### # end of boot loader objects... these are just some utility functions used -# return (disk, partition number) eg ('hda', 1) -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/')): - if dev[-2] == 'p': - cut = -1 - elif dev[-3] == 'p': - cut = -2 - else: - if dev[-2] in string.digits: - cut = -2 - elif dev[-1] in string.digits: - cut = -1 - - name = dev[:cut] - - # hack off the trailing 'p' from /dev/cciss/*, for example - if name[-1] == 'p': - for letter in name: - if letter not in string.letters and letter != "/": - name = name[:-1] - break - - if cut < 0: - partNum = int(dev[cut:]) - 1 - else: - partNum = None - - return (name, partNum) - def rootIsDevice(dev): if dev.startswith("LABEL=") or dev.startswith("UUID="): return False @@ -2129,35 +2096,3 @@ def getRootDevName(initrd, fsset, rootDev, instRoot): return "/dev/%s" %(rootDev,) except: return "/dev/%s" %(rootDev,) - -# returns a product name to use for the boot loader string -# FIXME: this is based on the stuff from anaconda, but kind of crappy :-/ -def getProductName(): - # try redhat-release first - if os.access("/etc/redhat-release", os.R_OK): - f = open("/etc/redhat-release", "r") - lines = f.readlines() - f.close() - for buf in lines: - relidx = buf.find(" release") - if relidx != -1: - return buf[:relidx] - - if os.access("/tmp/product/.buildstamp", os.R_OK): - path = "/tmp/product/.buildstamp" - elif os.access("/.buildstamp", os.R_OK): - path = "/.buildstamp" - else: - path = None - - if path is not None: - f = open(path, "r") - lines = f.readlines() - f.close() - if len(lines) >= 2: - return lines[1][:-1] - - # fall back - return "Red Hat Linux" - - diff --git a/booty/checkbootloader.py b/booty/checkbootloader.py index 38cb8255d..aa0ccd0ba 100644 --- a/booty/checkbootloader.py +++ b/booty/checkbootloader.py @@ -19,46 +19,14 @@ import os import string import rhpl +from fsset import getDiskPart +import iutil + grubConfigFile = "/etc/grub.conf" liloConfigFile = "/etc/lilo.conf" yabootConfigFile = "/etc/yaboot.conf" siloConfigFile = "/etc/silo.conf" - -# XXX: this is cut and pasted directly from booty/bootloaderInfo.py -# should eventually just go from there -def getDiskPart(dev): - """Return (disk, partition number) tuple for dev""" - cut = len(dev) - if (dev[:3] == "rd/" or dev[:4] == "ida/" or - dev[:6] == "cciss/"): - if dev[-2] == 'p': - cut = -1 - elif dev[-3] == 'p': - cut = -2 - else: - if dev[-2] in string.digits: - cut = -2 - elif dev[-1] in string.digits: - cut = -1 - - name = dev[:cut] - - # hack off the trailing 'p' from /dev/cciss/*, for example - if name[-1] == 'p': - for letter in name: - if letter not in string.letters and letter != "/": - name = name[:-1] - break - - if cut < 0: - partNum = int(dev[cut:]) - 1 - else: - partNum = None - - return (name, partNum) - - def getRaidDisks(raidDevice, raidLevel=None, stripPart=1): rc = [] if raidLevel is not None: @@ -140,22 +108,6 @@ def getBootDevList(line): rets.append(dev) return string.join(rets) -efi = None -## Determine if the hardware supports EFI. -# @return True if so, False otherwise. -def isEfi(): - global efi - if efi is not None: - return efi - - efi = False - if rhpl.getArch() in ("ia64", "i386", "x86_64"): - # XXX need to make sure efivars is loaded... - if os.path.exists("/sys/firmware/efi"): - efi = True - - return efi - def getBootloaderTypeAndBoot(instRoot = "/"): haveGrubConf = 1 haveLiloConf = 1 @@ -194,7 +146,7 @@ def getBootloaderTypeAndBoot(instRoot = "/"): if bootDev is not None: break - if isEfi(): + if iutil.isEfi(): return ("GRUB", bootDev) if bootDev is not None: -- cgit From 8e7bd7e0351034f608c859e51e39a0f57cc05136 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Feb 2009 14:34:06 -0500 Subject: No one should ever need to use MILO ever again. --- booty/bootloaderInfo.py | 33 +++------------------------------ 1 file changed, 3 insertions(+), 30 deletions(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 36daa5637..fc6cc718e 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -1458,25 +1458,6 @@ class alphaBootloaderInfo(bootloaderInfo): (foo, partitionNumber) = getDiskPart(path) return partitionNumber + 1 - # See if we would have to use MILO. MILO isn't supported by Red Hat. - def useMilo (self): - try: - f = open ('/proc/cpuinfo', 'ro') - except: - return - lines = f.readlines () - f.close() - serial = "" - for line in lines: - if line.find("system serial number") != -1: - serial = string.strip (string.split (line, ':')[1]) - break - - if serial and len (serial) >= 4 and serial.startswith("MILO"): - return 1 - else: - return 0 - def writeAboot(self, instRoot, fsset, bl, langs, kernelList, chainList, defaultDev, justConfig): # Get bootDevice and rootDevice @@ -1595,17 +1576,9 @@ class alphaBootloaderInfo(bootloaderInfo): if len(kernelList) < 1: self.noKernelsWarn(intf) - if self.useMilo(): - intf.messageWindow(_("MILO Not Supported"), - "This system requires the support of MILO to " + - "boot linux (MILO is not included in this " + - "distribution.) The installation of the " + - "bootloader can't be completed on this system.") - # elif not justConfig FIXME. - else: - self.writeAboot(instRoot, fsset, bl, langs, kernelList, - chainList, defaultDev, justConfig) - + self.writeAboot(instRoot, fsset, bl, langs, kernelList, + chainList, defaultDev, justConfig) + def __init__(self): bootloaderInfo.__init__(self) self.useGrubVal = 0 -- cgit From a4a5af7094e7a1aa74028295ef85bd23ac19eaac Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Feb 2009 14:46:49 -0500 Subject: Remove the unused needsEnterpriseKernel method. --- booty/lilo.py | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/booty/lilo.py b/booty/lilo.py index 5f667e557..dc2328e63 100644 --- a/booty/lilo.py +++ b/booty/lilo.py @@ -51,24 +51,6 @@ class UserDictCase(UserDict): def dict(self): return self.data - -def needsEnterpriseKernel(): - rc = 0 - - try: - f = open("/proc/e820info", "r") - except IOError: - return 0 - for l in f.readlines(): - l = string.split(l) - if l[3] == '(reserved)': continue - - regionEnd = (string.atol(l[0], 16) - 1) + string.atol(l[2], 16) - if regionEnd > 0xffffffffL: - rc = 1 - - return rc - class LiloConfigFile: """class representing a lilo.conf lilo configuration file. Used to manipulate the file directly""" -- cgit From ef779c8ed26c199ec26967696487099e6dbe3820 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Feb 2009 14:57:00 -0500 Subject: Remove a whole lot of unused code from booty.py. --- booty/booty.py | 291 --------------------------------------------------------- 1 file changed, 291 deletions(-) diff --git a/booty/booty.py b/booty/booty.py index b8045bc45..7ada90f2e 100644 --- a/booty/booty.py +++ b/booty/booty.py @@ -50,187 +50,6 @@ def getBootloader(): else: return bootloaderInfo() - -# install the kernels in kernelList to your bootloader's config file -# (if needed) -# kernelList is a python list of (kernelVersion, extraInfo) tuples -# backupSuffix is the suffix to use backing up the config file if -# we change it -# test is whether or not this is test mode -# filename is a filename to use instead of the default (testing only) -def installNewKernelImages(kernelList, backupSuffix = "rpmsave", test = 0, - filename = None): - """Add the kernels in kernelList to the current boot loader config file""" - - if rhpl.getArch() == 'i386': - return __installNewKernelImagesX86(kernelList, backupSuffix, test, - filename) - elif rhpl.getArch() == 'ia64': - return __installNewKernelImagesIA64(kernelList, backupSuffix, test, - filename) - elif rhpl.getArch() == 'ppc': - return __installNewKernelImagesPPC(kernelList, backupSuffix, test, - filename) - elif rhpl.getArch() == 'sparc': - return __installNewKernelImagesSparc(kernelList, backupSuffix, test, - filename) - else: - raise RuntimeError, "Don't know how to add new kernels for %s" % \ - (rhpl.getArch(),) - - -def __installNewKernelImagesX86(kernelList, backupSuffix, test, filename): - theBootloader = checkbootloader.whichBootLoader() - if theBootloader == "GRUB": - __installNewKernelImagesX86Grub(kernelList, backupSuffix, test) - else: - raise RuntimeError, "Cannot determine x86 bootloader in use." - -def __installNewKernelImagesX86Grub(kernelList, backupSuffix, test): - # get the current default kernel in the grub config - def getGrubDefault(): - pipe = os.popen("/sbin/grubby --default-kernel") - ret = pipe.read() - ret = string.strip(ret) - - return ret - - # set the default kernel in grub to the one at path - def setGrubDefault(path, instRoot="/"): - args = [instRoot + "/sbin/grubby", "--set-default", path] - ret = rhpl.executil.execWithRedirect(args[0], args, - stdout = None, stderr = None) - - return ret - - defaultImage = getGrubDefault() - if test: - print "defaultImage is %s" % (defaultImage,) - - # if we don't get any sort of answer back, do nothing - if defaultImage: - defaultType = getDefaultKernelType(defaultImage) - - # look for a kernel image of the same type - for (newVersion, imageType) in kernelList: - if defaultType == imageType: - if test: - print "Would have set default to /boot/vmlinuz-%s" % (newVersion,) - else: - setGrubDefault("/boot/vmlinuz-%s" % (newVersion,)) - - - -def __installNewKernelImagesIA64(kernelList, backupSuffix, test, filename): - if not filename: - filename = "/boot/efi/elilo.conf" - - config = updateLiloishConfigFile(kernelList, "/boot/efi/vmlinuz-%s", - test, filename) - - backup = writeConfig(config, filename, backupSuffix, test) - -def __installNewKernelImagesPPC(kernelList, backupSuffix, test, filename): - if not filename: - filename = "/etc/yaboot.conf" - - config = updateLiloishConfigFile(kernelList, "/boot/vmlinu-%s", - test, filename) - - backup = writeConfig(config, filename, backupSuffix, test) - - ret = yabootInstall("/") - if ret: - restoreBackup(filename, backup) - raise RuntimeError, "Real install of yaboot failed" - -def __installNewKernelImagesSparc(kernelList, backupSuffix, test, filename): - if not filename: - filename = "/etc/silo.conf" - - config = updateLiloishConfigFile(kernelList, "/boot/vmlinuz-%s", - test, filename) - - backup = writeConfig(config, filename, backupSuffix, test) - - ret = siloInstall("/") - if ret: - restoreBackup(filename, backup) - raise RuntimeError, "Real install of silo failed" - -# used for updating lilo-ish config files (eg elilo as well) -def updateLiloishConfigFile(kernelList, kernelPathFormat, test, configFile): - config = lilo.LiloConfigFile() - - # XXX should be able to create if one doesn't exist - if not os.access(configFile, os.R_OK): - return None - - # read in the config file and make sure we don't have any unsupported - # options - config.read(configFile) - if len(config.unsupported): - raise RuntimeError, ("Unsupported options in config file: %s" % - (config.unsupported,)) - - default = config.getDefaultLinux() - if not default: - raise RuntimeError, "Unable to find default linux entry" - - realDefault = config.getDefault() - setdefault = None - - defaultType = getDefaultKernelType(default.path) - rootDev = default.getEntry("root") - - for (newVersion, imageType) in kernelList: - path = kernelPathFormat % (newVersion,) - - initrd = makeInitrd("-%s" % (newVersion,), "/") - - if not os.access(initrd, os.R_OK): - initrd = None - - if imageType and imageType != defaultType: - # linux-smp.linux-BOOT, etc - label = "linux-"+imageType - elif not imageType and defaultType: - label = "linux-up" - else: - label = "linux" - - if label in config.listImages(): - backup = backupLabel(label, config.listImages()) - oldImage = config.getImage(label)[1] - oldImage.addEntry("label", backup) - if test: - print "Renamed %s to %s" % (label, backup) - - # alikins did this once, I'm not doing it again - katzj - if defaultType: - if imageType: - if defaultType == imageType: - setdefault = label - else: - if not imageType: - setdefault = label - - addImage(path, initrd, label, config, default) - - # if the default linux image's label is the same as the real default's - # label, then set what we've figured out as the default to be the - # default - if (default.getEntry('label') == realDefault.getEntry('label')) \ - and setdefault: - config.addEntry("default", setdefault) - else: # make sure the default entry is explicitly set - config.addEntry("default", realDefault.getEntry('label')) - - if test: - print config - return config - - # path is the path to the new kernel image # initrd is the path to the initrd (or None if it doesn't exist) # label is the label for the image @@ -273,113 +92,3 @@ def makeInitrd (kernelTag, instRoot): initrd = "/boot/initrd%s.img" % (kernelTag, ) return initrd - - -# determine the type of the default kernel -# kernelPath is the path of the current default -def getDefaultKernelType(kernelPath): - # XXX this isn't an ideal way to do this. up2date was poking at the - # rpmdb. this is a simple but stupid way to figure out the simple - # cases for now - defaultType = None - if kernelPath[-3:] == "smp": - defaultType = "smp" - elif kernelPath[-10:] == "enterprise": - defaultType = "enterprise" - elif kernelPath[-4:] == "BOOT": - defaultType = "BOOT" - elif kernelPath[-3:] == "ans": - defaultType = "ans" - elif kernelPath[-4:] == "briq": - defaultType = "briq" - elif kernelPath[-5:] == "teron": - defaultType = "teron" - elif kernelPath[-7:] == "pseries": - defaultType = "pseries" - elif kernelPath[-7:] == "iseries": - defaultType = "iseries" - else: - defaultType = None - - return defaultType - - -# make a silly .bak entry -def backupLabel(label, imageList): - backup = "%s.bak" % (label,) - while backup in imageList: - backup = backup + "_" - - # truncate long names. - if len(backup) > 32: - backup = backup[:28]+".bak" - - if backup in imageList: - raise RuntimeError, "Attempts to create unique backup label for %s failed" % (label,) - - return backup - - -def writeConfig(config, filename, backupSuffix, test = 0): - # write out the LILO config - try: - backup = backupFile(filename, backupSuffix) - liloperms = stat.S_IMODE(os.stat(filename)[stat.ST_MODE]) - if test: - filename = "/tmp/lilo.conf" - - config.write(filename, perms = liloperms) - except: - restoreBackup(filename, backup) - raise RuntimeError, "Error installing updated config file. Aborting" - - return backup - - -def backupFile(filename, suffix): - backup = "%s.%s-%s" % (filename, suffix, repr(time.time())) - - # make sure the backup file doesn't exist... add _'s until it doesn't - while os.access(backup, os.F_OK): - backup = backup + "_" - - shutil.copy(filename, backup) - - return backup - - -def restoreBackup(filename, backup): - shutil.copy(backup, filename) - os.remove(backup) - - -def liloInstall(instRoot, test = 0, filename = None, testFlag = 0): - args = [ instRoot + "/sbin/lilo", "-r", instRoot ] - - if test: - args.extend(["-t"]) - - if testFlag: - print args - ret = 0 - else: - ret = rhpl.executil.execWithRedirect(args[0], args, - stdout = None, stderr = None) - - return ret - -def yabootInstall(instRoot, filename = None): - args = [ instRoot + "/usr/sbin/ybin", "-r", instRoot ] - - ret = rhpl.executil.execWithRedirect(args[0], args, - stdout = None, stderr = None) - - return ret - -def siloInstall(instRoot, filename = None): - args = [instRoot + "/sbin/silo", "-r", instRoot] - - ret = rhpl.executil.execWithRedirect(args[0], args, - stdout = None, stderr = None) - - return ret -- cgit From ee28d0a243b5099d831aa6079b146f1bcf67deda Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Feb 2009 14:58:52 -0500 Subject: Remove the unused whichBootLoader method. --- booty/checkbootloader.py | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/booty/checkbootloader.py b/booty/checkbootloader.py index aa0ccd0ba..68cb8e13f 100644 --- a/booty/checkbootloader.py +++ b/booty/checkbootloader.py @@ -206,17 +206,3 @@ def getBootloaderTypeAndBoot(instRoot = "/"): return ("SILO", bootDev) return (None, None) - -def whichBootLoader(instRoot = "/"): - ret = getBootloaderTypeAndBoot(instRoot) - if not ret: - return None - else: - return ret[0] - -if __name__ == "__main__": - bootloader = whichBootLoader() - if bootloader: - print "Found %s." % (bootloader) - else: - print "Unable to determine boot loader." -- cgit From 4055441f79f5075236d2221d2d7add0416313e9a Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Feb 2009 15:46:09 -0500 Subject: makeInitrd makes more sense as a method on a bootloaderInfo subclass. --- booty/bootloaderInfo.py | 20 +++++++++++++------- booty/booty.py | 11 ----------- 2 files changed, 13 insertions(+), 18 deletions(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index fc6cc718e..20bb87e8c 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -300,6 +300,9 @@ class bootloaderInfo: else: self.defaultDevice = "partition" + def makeInitrd(self, kernelTag): + return "/boot/initrd%s.img" % kernelTag + # XXX need to abstract out the requirement for a fsset to be able # to get it "on the fly" on a running system as well as being able # to get it how we do now from anaconda. probably by having the @@ -355,7 +358,7 @@ class bootloaderInfo: sl = LiloConfigFile(imageType = "image", path = kernelFile) - initrd = booty.makeInitrd (kernelTag, instRoot) + initrd = self.makeInitrd(kernelTag) sl.addEntry("label", label) if os.access (instRoot + initrd, os.R_OK): @@ -803,7 +806,7 @@ class grubBootloaderInfo(bootloaderInfo): kernelTag = "-" + version kernelFile = "%svmlinuz%s" % (cfPath, kernelTag) - initrd = booty.makeInitrd (kernelTag, instRoot) + initrd = self.makeInitrd(kernelTag) f.write('title %s (%s)\n' % (longlabel, version)) f.write('\troot %s\n' % self.grubbyPartitionName(bootDevs[0])) @@ -1267,6 +1270,9 @@ class ia64BootloaderInfo(efiBootloaderInfo): self.removeOldEfiEntries(instRoot) self.addNewEfiEntry(instRoot, fsset) + def makeInitrd(self, kernelTag): + return "/boot/efi/EFI/redhat/initrd%s.img" % kernelTag + def __init__(self): efiBootloaderInfo.__init__(self) self._configname = "elilo.conf" @@ -1315,7 +1321,7 @@ class s390BootloaderInfo(bootloaderInfo): sl = LiloConfigFile(imageType = "image", path = kernelFile) - initrd = booty.makeInitrd (kernelTag, instRoot) + initrd = self.makeInitrd(kernelTag) sl.addEntry("label", label) if os.access (instRoot + initrd, os.R_OK): @@ -1411,7 +1417,7 @@ class s390BootloaderInfo(bootloaderInfo): kernelTag = "-" + version kernelFile = "%svmlinuz%s" % (cfPath, kernelTag) - initrd = booty.makeInitrd (kernelTag, instRoot) + initrd = self.makeInitrd(kernelTag) f.write('[%s]\n' % (label)) f.write('\timage=%s\n' % (kernelFile)) if os.access (instRoot + initrd, os.R_OK): @@ -1523,7 +1529,7 @@ class alphaBootloaderInfo(bootloaderInfo): f.write("%d:%d%s" %(lines, bpn, kernelFile)) # See if we can come up with an initrd argument that exists - initrd = booty.makeInitrd (kernelTag, instRoot) + initrd = self.makeInitrd(kernelTag) if os.path.isfile(instRoot + initrd): f.write(" initrd=%sinitrd%s.img" %(kernelPath, kernelTag)) @@ -1700,7 +1706,7 @@ class ppcBootloaderInfo(bootloaderInfo): f.write("\tlabel=%s\n" %(label,)) f.write("\tread-only\n") - initrd = booty.makeInitrd(kernelTag, instRoot) + initrd = self.makeInitrd(kernelTag) if os.access(instRoot + initrd, os.R_OK): f.write("\tinitrd=%s/initrd%s.img\n" %(cfPath,kernelTag)) @@ -1975,7 +1981,7 @@ class sparcBootloaderInfo(bootloaderInfo): f.write("\tlabel=%s\n" % (label,)) f.write("\tread-only\n") - initrd = booty.makeInitrd(kernelTag, instRoot) + initrd = self.makeInitrd(kernelTag) if os.access(instRoot + initrd, os.R_OK): f.write("\tinitrd=%s/initrd%s.img\n" % (cfPath, kernelTag)) diff --git a/booty/booty.py b/booty/booty.py index 7ada90f2e..75d4e7fc9 100644 --- a/booty/booty.py +++ b/booty/booty.py @@ -81,14 +81,3 @@ def addImage(path, initrd, label, config, default): entry.addEntry(key, default.getEntry(key)) config.addImage(entry, 1) - - -# note that this function no longer actually creates an initrd. -# the kernel's %post does this now -def makeInitrd (kernelTag, instRoot): - if rhpl.getArch() == 'ia64': - initrd = "/boot/efi/EFI/redhat/initrd%s.img" % (kernelTag, ) - else: - initrd = "/boot/initrd%s.img" % (kernelTag, ) - - return initrd -- cgit From 912cc2f9d5026b0e79f34be46eebc1bda8e3b0b4 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Feb 2009 15:49:01 -0500 Subject: Reduce booty.py down to the bare minimum. --- booty/__init__.py | 47 +++++++++++++++++++++++++++++++ booty/booty.py | 83 ------------------------------------------------------- 2 files changed, 47 insertions(+), 83 deletions(-) create mode 100644 booty/__init__.py delete mode 100644 booty/booty.py diff --git a/booty/__init__.py b/booty/__init__.py new file mode 100644 index 000000000..c15ca6d06 --- /dev/null +++ b/booty/__init__.py @@ -0,0 +1,47 @@ +# +# bootloader.py - generic boot loader handling backend for up2date and anaconda +# +# Jeremy Katz +# Adrian Likins +# Peter Jones +# +# Copyright 2001-2005 Red Hat, Inc. +# +# This software may be freely redistributed under the terms of the GNU +# library public license. +# +# 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. +# +"""Module for manipulation and creation of boot loader configurations""" + +import rhpl +from bootloaderInfo import * + +# return instance of the appropriate bootloader for our arch +def getBootloader(): + """Get the bootloader info object for your architecture""" + if rhpl.getArch() == 'i386': + import x86 + return x86.x86BootloaderInfo() + elif rhpl.getArch() == 'ia64': + import ia64 + return ia64.ia64BootloaderInfo() + elif rhpl.getArch() == 's390' or rhpl.getArch() == "s390x": + import s390 + return s390.s390BootloaderInfo() + elif rhpl.getArch() == "alpha": + import alpha + return alpha.alphaBootloaderInfo() + elif rhpl.getArch() == "x86_64": + import x86 + return x86.x86BootloaderInfo() + elif rhpl.getArch() == "ppc": + import pcc + return ppc.ppcBootloaderInfo() + elif rhpl.getArch() == "sparc": + import sparc + return sparc.sparcBootloaderInfo() + else: + return bootloaderInfo() diff --git a/booty/booty.py b/booty/booty.py deleted file mode 100644 index 75d4e7fc9..000000000 --- a/booty/booty.py +++ /dev/null @@ -1,83 +0,0 @@ -# -# bootloader.py - generic boot loader handling backend for up2date and anaconda -# -# Jeremy Katz -# Adrian Likins -# Peter Jones -# -# Copyright 2001-2005 Red Hat, Inc. -# -# This software may be freely redistributed under the terms of the GNU -# library public license. -# -# 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. -# -"""Module for manipulation and creation of boot loader configurations""" - -import os, sys -import stat -import time -import shutil -import lilo - -import checkbootloader -import rhpl -import rhpl.executil -from bootloaderInfo import * - - -# return instance of the appropriate bootloader for our arch -def getBootloader(): - """Get the bootloader info object for your architecture""" - if rhpl.getArch() == 'i386': - return x86BootloaderInfo() - elif rhpl.getArch() == 'ia64': - return ia64BootloaderInfo() - elif rhpl.getArch() == 's390' or rhpl.getArch() == "s390x": - return s390BootloaderInfo() - elif rhpl.getArch() == "alpha": - return alphaBootloaderInfo() - elif rhpl.getArch() == "x86_64": - return x86BootloaderInfo() - elif rhpl.getPPCMachine() == "iSeries": - return iseriesBootloaderInfo() - elif rhpl.getArch() == "ppc": - return ppcBootloaderInfo() - elif rhpl.getArch() == "sparc": - return sparcBootloaderInfo() - else: - return bootloaderInfo() - -# path is the path to the new kernel image -# initrd is the path to the initrd (or None if it doesn't exist) -# label is the label for the image -# config is the full LiloConfigFile object for the config file -# default is the LiloConfigFile object for the default Linux-y entry -def addImage(path, initrd, label, config, default): - # these directives must be on a per-image basis and are non-sensical - # otherwise - dontCopy = ['initrd', 'alias', 'label'] - - entry = lilo.LiloConfigFile(imageType = "image", path = path) - - if label: - entry.addEntry("label", label) - else: - raise RuntimeError, "Unable to determine a label for %s. Aborting" % (path,) - - if initrd: - entry.addEntry("initrd", initrd) - - # go through all of the things listed for the default - # config entry and if they're not in our blacklist - # of stuff not to copy, go ahead and copy it - entries = default.listEntries() - for key in entries.keys(): - if key in dontCopy: - pass - else: - entry.addEntry(key, default.getEntry(key)) - - config.addImage(entry, 1) -- cgit From cb99be895352cedbc60404bb1cf2294df7cf80bf Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Feb 2009 16:05:21 -0500 Subject: Use flags.cmdline instead of opening /proc/cmdline. --- booty/bootloaderInfo.py | 61 +++++++++++++++++++------------------------------ 1 file changed, 24 insertions(+), 37 deletions(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 20bb87e8c..f29d75c8f 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -30,6 +30,7 @@ import rhpl from rhpl.translate import _, N_ import rhpl.executil +from flags import flags from fsset import getDiskPart import iutil from product import * @@ -133,16 +134,17 @@ class KernelArguments: f.close() # look for kernel arguments we know should be preserved and add them - ourargs = ["speakup_synth=", "apic", "noapic", "apm=", "ide=nodma", - "noht", "acpi=", "video=", "pci=", "nodmraid", "nompath"] - f = open("/proc/cmdline") - cmdline = f.read()[:-1] - f.close() - cmdlineargs = cmdline.split(" ") - for arg in cmdlineargs: - for check in ourargs: - if arg.startswith(check): - newArgs.append(arg) + ourargs = ["speakup_synth", "apic", "noapic", "apm", "ide", "noht", + "acpi", "video", "pci", "nodmraid", "nompath"] + for arg in ourargs: + if not flags.cmdline.has_key(arg): + continue + + val = flags.cmdline.get(arg, "") + if val: + newArgs.append("%s=%s" % (arg, val)) + else: + newArgs.append(arg) self.args = " ".join(newArgs) @@ -528,31 +530,20 @@ class bootloaderInfo: self._drivelist = None - from flags import flags if flags.serial != 0: - # now look at /proc/cmdline to pull any serial console - # args - f = open("/proc/cmdline", "r") - cmdline = f.read()[:-1] - f.close() - options = "" - device = None - cmdlineargs = cmdline.split(" ") - for arg in cmdlineargs: - # found a console argument - if arg.startswith("console="): - (foo, console) = arg.split("=") - # the options are everything after the comma - comma = console.find(",") - if comma != -1: - options = console[comma:] - device = console[:comma] - else: - options = "" - device = console - - if device is None and rhpl.getArch() != "ia64": + device = "" + console = flags.get("console", "") + + # the options are everything after the comma + comma = console.find(",") + if comma != -1: + options = console[comma:] + device = console[:comma] + else: + device = console + + if not device and rhpl.getArch() != "ia64": self.serialDevice = "ttyS0" self.serialOptions = "" else: @@ -1631,8 +1622,6 @@ class ppcBootloaderInfo(bootloaderInfo): def writeYaboot(self, instRoot, fsset, bl, langs, kernelList, chainList, defaultDev, justConfigFile): - from flags import flags - yabootTarget = string.join(self.getBootDevs(fsset, bl)) bootDev = fsset.getEntryByMountPoint("/boot") @@ -1931,8 +1920,6 @@ class sparcBootloaderInfo(bootloaderInfo): def writeSilo(self, instRoot, fsset, bl, langs, kernelList, chainList, defaultDev, justConfigFile): - from flags import flags - bootDev = fsset.getEntryByMountPoint("/boot") mf = '/silo.message' if bootDev: -- cgit From c37f42fe680ebc19a6ea550b0518c8a86a78d8b6 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Feb 2009 16:06:40 -0500 Subject: Don't use rhpl.log anywhere, not even in comments. --- booty/bootloaderInfo.py | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index f29d75c8f..2fc3ed129 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -26,7 +26,6 @@ from copy import copy from lilo import LiloConfigFile import rhpl -#from rhpl.log import log from rhpl.translate import _, N_ import rhpl.executil @@ -130,7 +129,6 @@ class KernelArguments: self.cargs.append(vparm) except Exception, e: pass - #log("exception parsing %s: %s" % (cfgFilename, e)) f.close() # look for kernel arguments we know should be preserved and add them @@ -227,7 +225,6 @@ class BootImages: devs.append((dev, type)) foundDos = 1 except Exception, e: - #log("exception checking %s: %s" %(dev, e)) pass elif ((type == 'ntfs' or type =='hpfs') and not foundDos and doesDualBoot()): @@ -576,7 +573,6 @@ class grubBootloaderInfo(bootloaderInfo): return if isCrypted and self.useGrubVal == 0: - #log("requested crypted password with lilo; ignoring") self.pure = None return elif isCrypted: @@ -628,9 +624,6 @@ class grubBootloaderInfo(bootloaderInfo): return [device] def runGrubInstall(self, instRoot, bootDev, cmds, cfPath): - #log("GRUB commands:") - #for cmd in cmds: - # log("\t%s\n", cmd) if cfPath == "/": syncDataToDisk(bootDev, "/boot", instRoot) else: @@ -1071,7 +1064,6 @@ class grubBootloaderInfo(bootloaderInfo): cmds.append(cmd) if not justConfigFile: - #log("GRUB command %s", cmd) self.runGrubInstall(instRoot, bootDev.device.setupDevice(), cmds, cfPath) @@ -1549,7 +1541,6 @@ class alphaBootloaderInfo(bootloaderInfo): # to and the second argument is a path to the bootstrap loader # file. args = ("swriteboot", ("/dev/%s" % wbd), "/boot/bootlx") - #log("swriteboot command: %s" %(args,)) rhpl.executil.execWithRedirect ('/sbin/swriteboot', args, root = instRoot, stdout = "/dev/tty5", @@ -1561,7 +1552,6 @@ class alphaBootloaderInfo(bootloaderInfo): # It's always the boot partition whether it's / or /boot (with # the mount point being omitted.) args = ("abootconf", ("/dev/%s" % wbd), str (bdpn)) - #log("abootconf command: %s" %(args,)) rhpl.executil.execWithRedirect ('/sbin/abootconf', args, root = instRoot, stdout = "/dev/tty5", @@ -1725,7 +1715,6 @@ class ppcBootloaderInfo(bootloaderInfo): ybinargs = [ yabootProg, "-f", "-C", cf ] - #log("running: %s" %(ybinargs,)) if not flags.test: rhpl.executil.execWithRedirect(ybinargs[0], ybinargs, @@ -1782,10 +1771,6 @@ class iseriesBootloaderInfo(bootloaderInfo): if len(kernelList) < 1: self.noKernelsWarn(intf) return - #if len(kernelList) > 1: - # # FIXME: how can this happen? - # log("more than one kernel on iSeries. bailing and just using " - # "the first") # iseries is Weird (tm) -- here's the basic theory # a) have /boot/vmlinitrd-$(version) @@ -1818,16 +1803,13 @@ class iseriesBootloaderInfo(bootloaderInfo): # OS/400 will load as NWSSTG bootDev = bl.getDevice() if bootDev: - #log("Writing kernel %s to PReP partition %s" %(kernelFile, bootDev)) try: self.ddFile(instRoot + kernelFile, "%s/dev/%s" %(instRoot, bootDev)) except Exception, e: # FIXME: should this be more fatal - #log("Failed to write kernel: %s" %(e,)) pass else: - #log("No PReP boot partition, not writing kernel for NWSSTG") pass @@ -1835,7 +1817,6 @@ class iseriesBootloaderInfo(bootloaderInfo): # into OS/400, so set up side C (used by default for NWSSTG) with # our current bits for side in ("C", "B"): - #log("Writing kernel and cmdline to side %s" %(side,)) wrotekernel = 0 try: self.ddFile(instRoot + kernelFile, @@ -1843,7 +1824,6 @@ class iseriesBootloaderInfo(bootloaderInfo): wrotekernel = 1 except Exception, e: # FIXME: should this be more fatal? - #log("Failed to write kernel to side %s: %s" %(side, e)) pass if wrotekernel == 1: @@ -1860,11 +1840,8 @@ class iseriesBootloaderInfo(bootloaderInfo): "%s/proc/iSeries/mf/%s/cmdline" %(instRoot, side)) except Exception, e: - #log("Failed to write kernel command line to side %s: %s" - # %(side, e)) pass - #log("Setting default side to C") f = open(instRoot + "/proc/iSeries/mf/side", "w") f.write("C") f.close() @@ -2005,7 +1982,6 @@ class sparcBootloaderInfo(bootloaderInfo): else: sbinargs += ["-U"] - #log("running: %s" % (sbinargs,)) if not flags.test: rhpl.executil.execWithRedirect(sbinargs[0], sbinargs, -- cgit From d3bcdfc90610f1d16a1d0d307b26cf10170a068a Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Feb 2009 16:53:20 -0500 Subject: Don't support /boot/message files in multiple languages. --- booty/bootloaderInfo.py | 76 +++++++++++++++++++++---------------------------- 1 file changed, 33 insertions(+), 43 deletions(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 2fc3ed129..9f1952ede 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -307,7 +307,7 @@ class bootloaderInfo: # to get it how we do now from anaconda. probably by having the # first thing in the chain create a "fsset" object that has the # dictionary of mounted filesystems since that's what we care about - def getBootloaderConfig(self, instRoot, fsset, bl, langs, kernelList, + def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, chainList, defaultDev): images = bl.images.getImages() @@ -425,10 +425,10 @@ class bootloaderInfo: return lilo - def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + def write(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig, intf = None): if len(kernelList) >= 1: - config = self.getBootloaderConfig(instRoot, fsset, bl, langs, + config = self.getBootloaderConfig(instRoot, fsset, bl, kernelList, chainList, defaultDev) config.write(instRoot + self.configfile, perms = self.perms) @@ -680,7 +680,7 @@ class grubBootloaderInfo(bootloaderInfo): self.runGrubInstall(instRoot, bootDev, cmds, cfPath) - def writeGrub(self, instRoot, fsset, bl, langs, kernelList, chainList, + def writeGrub(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfigFile): images = bl.images.getImages() @@ -937,11 +937,10 @@ class grubBootloaderInfo(bootloaderInfo): return "(%s)" %(self.grubbyDiskName(name)) - def getBootloaderConfig(self, instRoot, fsset, bl, langs, kernelList, + def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, chainList, defaultDev): config = bootloaderInfo.getBootloaderConfig(self, instRoot, fsset, - bl, langs, - kernelList, chainList, + bl, kernelList, chainList, defaultDev) liloTarget = bl.getDevice() @@ -954,14 +953,6 @@ class grubBootloaderInfo(bootloaderInfo): if self.pure is not None and not self.useGrubVal: config.addEntry("restricted", replace = 0) config.addEntry("password", self.pure, replace = 0) - - - import language - for lang in language.expandLangs(langs.getDefault()): - fn = "/boot/message." + lang - if os.access(instRoot + fn, os.R_OK): - message = fn - break if self.serial == 1: # grab the 0-based number of the serial console device @@ -985,7 +976,7 @@ class grubBootloaderInfo(bootloaderInfo): # this is a hackish function that depends on the way anaconda writes # out the grub.conf with a #boot= comment # XXX this falls into the category of self.doUpgradeOnly - def upgradeGrub(self, instRoot, fsset, bl, langs, kernelList, chainList, + def upgradeGrub(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfigFile): if justConfigFile: return "" @@ -1081,7 +1072,7 @@ class grubBootloaderInfo(bootloaderInfo): f.write("forcelba=0\n") f.close() - def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + def write(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig, intf): if self.timeout is None and chainList: self.timeout = 5 @@ -1089,14 +1080,14 @@ class grubBootloaderInfo(bootloaderInfo): # XXX HACK ALERT - see declaration above if self.doUpgradeOnly: if self.useGrubVal: - self.upgradeGrub(instRoot, fsset, bl, langs, kernelList, + self.upgradeGrub(instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig) return if len(kernelList) < 1: self.noKernelsWarn(intf) - out = self.writeGrub(instRoot, fsset, bl, langs, kernelList, + out = self.writeGrub(instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig | (not self.useGrubVal)) @@ -1200,9 +1191,9 @@ class efiBootloaderInfo(bootloaderInfo): self.kernelLocation = "" class x86BootloaderInfo(grubBootloaderInfo, efiBootloaderInfo): - def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + def write(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig, intf): - grubBootloaderInfo.write(self, instRoot, fsset, bl, langs, kernelList, + grubBootloaderInfo.write(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig, intf) # XXX move the lilo.conf out of the way if they're using GRUB @@ -1223,29 +1214,28 @@ class x86BootloaderInfo(grubBootloaderInfo, efiBootloaderInfo): efiBootloaderInfo.__init__(self, initialize=False) class ia64BootloaderInfo(efiBootloaderInfo): - def getBootloaderConfig(self, instRoot, fsset, bl, langs, kernelList, + def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, chainList, defaultDev): config = bootloaderInfo.getBootloaderConfig(self, instRoot, fsset, - bl, langs, - kernelList, chainList, + bl, kernelList, chainList, defaultDev) # altix boxes need relocatable (#120851) config.addEntry("relocatable") return config - def writeLilo(self, instRoot, fsset, bl, langs, kernelList, + def writeLilo(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig): - config = self.getBootloaderConfig(instRoot, fsset, bl, langs, + config = self.getBootloaderConfig(instRoot, fsset, bl, kernelList, chainList, defaultDev) config.write(instRoot + self.configfile, perms = 0755) return "" - def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + def write(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig, intf): if len(kernelList) >= 1: - out = self.writeLilo(instRoot, fsset, bl, langs, kernelList, + out = self.writeLilo(instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig) else: self.noKernelsWarn(intf) @@ -1262,7 +1252,7 @@ class ia64BootloaderInfo(efiBootloaderInfo): self._bootloader = "elilo.efi" class s390BootloaderInfo(bootloaderInfo): - def getBootloaderConfig(self, instRoot, fsset, bl, langs, kernelList, + def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, chainList, defaultDev): images = bl.images.getImages() @@ -1377,7 +1367,7 @@ class s390BootloaderInfo(bootloaderInfo): return "" - def writeZipl(self, instRoot, fsset, bl, langs, kernelList, chainList, + def writeZipl(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfigFile): images = bl.images.getImages() rootDev = fsset.getEntryByMountPoint("/").device.getDevice() @@ -1422,9 +1412,9 @@ class s390BootloaderInfo(bootloaderInfo): return "" - def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + def write(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig, intf): - out = self.writeZipl(instRoot, fsset, bl, langs, kernelList, + out = self.writeZipl(instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig | (not self.useZiplVal)) out = self.writeChandevConf(bl, instRoot) @@ -1447,7 +1437,7 @@ class alphaBootloaderInfo(bootloaderInfo): (foo, partitionNumber) = getDiskPart(path) return partitionNumber + 1 - def writeAboot(self, instRoot, fsset, bl, langs, kernelList, + def writeAboot(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig): # Get bootDevice and rootDevice rootDevice = fsset.getEntryByMountPoint("/").device.getDevice() @@ -1558,12 +1548,12 @@ class alphaBootloaderInfo(bootloaderInfo): stderr = "/dev/tty5") - def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + def write(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig, intf): if len(kernelList) < 1: self.noKernelsWarn(intf) - self.writeAboot(instRoot, fsset, bl, langs, kernelList, + self.writeAboot(instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig) def __init__(self): @@ -1609,7 +1599,7 @@ class ppcBootloaderInfo(bootloaderInfo): return devs - def writeYaboot(self, instRoot, fsset, bl, langs, kernelList, + def writeYaboot(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfigFile): yabootTarget = string.join(self.getBootDevs(fsset, bl)) @@ -1734,10 +1724,10 @@ class ppcBootloaderInfo(bootloaderInfo): # or not self.password = val - def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + def write(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig, intf): if len(kernelList) >= 1: - out = self.writeYaboot(instRoot, fsset, bl, langs, kernelList, + out = self.writeYaboot(instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig) else: self.noKernelsWarn(intf) @@ -1766,7 +1756,7 @@ class iseriesBootloaderInfo(bootloaderInfo): return size - def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + def write(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig, intf): if len(kernelList) < 1: self.noKernelsWarn(intf) @@ -1856,7 +1846,7 @@ class isolinuxBootloaderInfo(bootloaderInfo): self.kernelLocation = "/boot" self.configfile = "/boot/isolinux/isolinux.cfg" - def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + def write(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig, intf = None): if not os.path.isdir(instRoot + "/boot/isolinux"): os.mkdir(instRoot + "/boot/isolinux") @@ -1894,7 +1884,7 @@ class isolinuxBootloaderInfo(bootloaderInfo): class sparcBootloaderInfo(bootloaderInfo): - def writeSilo(self, instRoot, fsset, bl, langs, kernelList, + def writeSilo(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfigFile): bootDev = fsset.getEntryByMountPoint("/boot") @@ -2000,10 +1990,10 @@ class sparcBootloaderInfo(bootloaderInfo): # silo just handles the password unencrypted self.password = val - def write(self, instRoot, fsset, bl, langs, kernelList, chainList, + def write(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig, intf): if len(kernelList) >= 1: - self.writeSilo(instRoot, fsset, bl, langs, kernelList, chainList, + self.writeSilo(instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfig) else: self.noKernelsWarn(intf) -- cgit From 810a7dfd37eb3c326430ede5acb03aba185426d7 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 19 Feb 2009 13:28:23 -0500 Subject: We no longer need special code for iSeries. --- booty/bootloaderInfo.py | 102 ------------------------------------------------ 1 file changed, 102 deletions(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 9f1952ede..2a6f561af 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -1738,108 +1738,6 @@ class ppcBootloaderInfo(bootloaderInfo): self.kernelLocation = "/boot" self.configfile = "/etc/yaboot.conf" - -class iseriesBootloaderInfo(bootloaderInfo): - def ddFile(self, inf, of, bs = 4096): - src = os.open(inf, os.O_RDONLY) - dest = os.open(of, os.O_WRONLY | os.O_CREAT) - size = 0 - - buf = os.read(src, bs) - while len(buf) > 0: - size = size + len(buf) - os.write(dest, buf) - buf = os.read(src, bs) - - os.close(src) - os.close(dest) - - return size - - def write(self, instRoot, fsset, bl, kernelList, chainList, - defaultDev, justConfig, intf): - if len(kernelList) < 1: - self.noKernelsWarn(intf) - return - - # iseries is Weird (tm) -- here's the basic theory - # a) have /boot/vmlinitrd-$(version) - # b) copy default kernel to PReP partition - # c) dd default kernel to /proc/iSeries/mf/C/vmlinux - # d) set cmdline in /boot/cmdline-$(version) - # e) copy cmdline to /proc/iSeries/mf/C/cmdline - # f) set default side to 'C' i /proc/iSeries/mf/side - # g) put default kernel and cmdline on side B too (#91038) - - rootDevice = fsset.getEntryByMountPoint("/").device.getDevice() - - # write our command line files - for (kernel, tag, kernelTag) in kernelList: - cmdFile = "%scmdline-%s" %(self.kernelLocation, kernelTag) - initrd = "%sinitrd-%s.img" %(self.kernelLocation, kernelTag) - realroot = getRootDevName(initrd, fsset, rootDevice, instRoot) - f = open(instRoot + cmdFile, "w") - f.write("ro root=%s" %(realroot,)) - if bl.args.get(): - f.write(" %s" %(bl.args.get(),)) - f.write("\n") - f.close() - os.chmod(instRoot + cmdFile, 0644) - - kernel, tag, kernelTag = kernelList[0] - kernelFile = "%svmlinitrd-%s" %(self.kernelLocation, kernelTag) - - # write the kernel to the PReP partition since that's what - # OS/400 will load as NWSSTG - bootDev = bl.getDevice() - if bootDev: - try: - self.ddFile(instRoot + kernelFile, "%s/dev/%s" %(instRoot, - bootDev)) - except Exception, e: - # FIXME: should this be more fatal - pass - else: - pass - - - # now, it's a lot faster to boot if we don't make people go back - # into OS/400, so set up side C (used by default for NWSSTG) with - # our current bits - for side in ("C", "B"): - wrotekernel = 0 - try: - self.ddFile(instRoot + kernelFile, - "%s/proc/iSeries/mf/%s/vmlinux" %(instRoot, side)) - wrotekernel = 1 - except Exception, e: - # FIXME: should this be more fatal? - pass - - if wrotekernel == 1: - try: - # blank it. ugh. - f = open("%s/proc/iSeries/mf/%s/cmdline" %(instRoot, side), - "w+") - f.write(" " * 255) - f.close() - - self.ddFile("%s/%scmdline-%s" %(instRoot, - self.kernelLocation, - kernelTag), - "%s/proc/iSeries/mf/%s/cmdline" %(instRoot, - side)) - except Exception, e: - pass - - f = open(instRoot + "/proc/iSeries/mf/side", "w") - f.write("C") - f.close() - - def __init__(self): - bootloaderInfo.__init__(self) - self.kernelLocation = "/boot/" - class isolinuxBootloaderInfo(bootloaderInfo): def __init__(self): bootloaderInfo.__init__(self) -- cgit From 2c8c10290688b9c2038b1448e7bed9723012641e Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 19 Feb 2009 13:48:41 -0500 Subject: isolinuxBootloaderInfo is unused now, too. --- booty/bootloaderInfo.py | 43 ------------------------------------------- 1 file changed, 43 deletions(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 2a6f561af..14d33835a 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -1738,49 +1738,6 @@ class ppcBootloaderInfo(bootloaderInfo): self.kernelLocation = "/boot" self.configfile = "/etc/yaboot.conf" -class isolinuxBootloaderInfo(bootloaderInfo): - def __init__(self): - bootloaderInfo.__init__(self) - self.kernelLocation = "/boot" - self.configfile = "/boot/isolinux/isolinux.cfg" - - def write(self, instRoot, fsset, bl, kernelList, chainList, - defaultDev, justConfig, intf = None): - if not os.path.isdir(instRoot + "/boot/isolinux"): - os.mkdir(instRoot + "/boot/isolinux") - - f = open(instRoot + "/boot/isolinux/isolinux.cfg", "w+") - f.write("# isolinux.cfg generated by anaconda\n\n") - - f.write("prompt 1\n") - f.write("timeout %s\n" % (self.timeout or 600)) - - # FIXME: as this stands, we can really only handle one due to - # filename length limitations with base iso9660. fun, fun. - for (label, longlabel, version) in kernelList: - # XXX hackity, hack hack hack. but we need them in a different - # path for live cd only - shutil.copy("%s/boot/vmlinuz-%s" %(instRoot, version), - "%s/boot/isolinux/vmlinuz" %(instRoot,)) - shutil.copy("%s/boot/initrd-%s.img" %(instRoot, version), - "%s/boot/isolinux/initrd.img" %(instRoot,)) - - # FIXME: need to dtrt for xen kernels with multiboot com32 module - f.write("label linux\n") - f.write("\tkernel vmlinuz\n") - f.write("\tappend initrd=initrd.img,initlive.gz\n") - f.write("\n") - - break - - f.close() - os.chmod(instRoot + "/boot/isolinux/isolinux.cfg", 0600) - - # copy the isolinux bin - shutil.copy(instRoot + "/usr/lib/syslinux/isolinux-debug.bin", - instRoot + "/boot/isolinux/isolinux.bin") - - class sparcBootloaderInfo(bootloaderInfo): def writeSilo(self, instRoot, fsset, bl, kernelList, chainList, defaultDev, justConfigFile): -- cgit From 38eee03b0803cc286f05c42c969912c576713685 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 19 Feb 2009 14:28:22 -0500 Subject: Move each bootloader class into its own file. --- booty/__init__.py | 1 + booty/alpha.py | 143 ++++++ booty/bootloaderInfo.py | 1264 +---------------------------------------------- booty/ia64.py | 39 ++ booty/ppc.py | 179 +++++++ booty/s390.py | 179 +++++++ booty/sparc.py | 127 +++++ booty/x86.py | 567 +++++++++++++++++++++ 8 files changed, 1256 insertions(+), 1243 deletions(-) create mode 100644 booty/alpha.py create mode 100644 booty/ia64.py create mode 100644 booty/ppc.py create mode 100644 booty/s390.py create mode 100644 booty/sparc.py create mode 100644 booty/x86.py diff --git a/booty/__init__.py b/booty/__init__.py index c15ca6d06..a5bc97a6a 100644 --- a/booty/__init__.py +++ b/booty/__init__.py @@ -18,6 +18,7 @@ import rhpl from bootloaderInfo import * +from bootloader import * # return instance of the appropriate bootloader for our arch def getBootloader(): diff --git a/booty/alpha.py b/booty/alpha.py new file mode 100644 index 000000000..5e62c93ca --- /dev/null +++ b/booty/alpha.py @@ -0,0 +1,143 @@ +import os +import rhpl.executil + +from bootloaderInfo import * +import fsset + +class alphaBootloaderInfo(bootloaderInfo): + def wholeDevice (self, path): + (device, foo) = fsset.getDiskPart(path) + return device + + def partitionNum (self, path): + # getDiskPart returns part numbers 0-based; we need it one based + # *sigh* + (foo, partitionNumber) = getDiskPart(path) + return partitionNumber + 1 + + def writeAboot(self, instRoot, fsset, bl, kernelList, + chainList, defaultDev, justConfig): + # Get bootDevice and rootDevice + rootDevice = fsset.getEntryByMountPoint("/").device.getDevice() + if fsset.getEntryByMountPoint("/boot"): + bootDevice = fsset.getEntryByMountPoint("/boot").device.getDevice() + else: + bootDevice = rootDevice + bootnotroot = bootDevice != rootDevice + + # If /etc/aboot.conf already exists we rename it + # /etc/aboot.conf.rpmsave. + if os.path.isfile(instRoot + self.configfile): + os.rename (instRoot + self.configfile, + instRoot + self.configfile + ".rpmsave") + + # Then we create the necessary files. If the root device isn't + # the boot device, we create /boot/etc/ where the aboot.conf + # will live, and we create /etc/aboot.conf as a symlink to it. + if bootnotroot: + # Do we have /boot/etc ? If not, create one + if not os.path.isdir (instRoot + '/boot/etc'): + os.mkdir(instRoot + '/boot/etc', 0755) + + # We install the symlink (/etc/aboot.conf has already been + # renamed in necessary.) + os.symlink("../boot" + self.configfile, instRoot + self.configfile) + + cfPath = instRoot + "/boot" + self.configfile + # Kernel path is set to / because a boot partition will + # be a root on its own. + kernelPath = '/' + # Otherwise, we just need to create /etc/aboot.conf. + else: + cfPath = instRoot + self.configfile + kernelPath = self.kernelLocation + + # If we already have an aboot.conf, rename it + if os.access (cfPath, os.R_OK): + self.perms = os.stat(cfPath)[0] & 0777 + os.rename(cfPath, cfPath + '.rpmsave') + + # Now we're going to create and populate cfPath. + f = open (cfPath, 'w+') + f.write ("# aboot default configurations\n") + + if bootnotroot: + f.write ("# NOTICE: You have a /boot partition. This means that\n") + f.write ("# all kernel paths are relative to /boot/\n") + + # bpn is the boot partition number. + bpn = self.partitionNum(bootDevice) + lines = 0 + + # We write entries line using the following format: + # root= [options] + # We get all the kernels we need to know about in kernelList. + + for (kernel, tag, version) in kernelList: + kernelTag = "-" + version + kernelFile = "%svmlinuz%s" %(kernelPath, kernelTag) + + f.write("%d:%d%s" %(lines, bpn, kernelFile)) + + # See if we can come up with an initrd argument that exists + initrd = self.makeInitrd(kernelTag) + if os.path.isfile(instRoot + initrd): + f.write(" initrd=%sinitrd%s.img" %(kernelPath, kernelTag)) + + realroot = getRootDevName(initrd, fsset, rootDevice, instRoot) + f.write(" root=%s" %(realroot,)) + + args = self.args.get() + if args: + f.write(" %s" %(args,)) + + f.write("\n") + lines = lines + 1 + + # We're done writing the file + f.close () + del f + + if not justConfig: + # Now we're ready to write the relevant boot information. wbd + # is the whole boot device, bdpn is the boot device partition + # number. + wbd = self.wholeDevice (bootDevice) + bdpn = self.partitionNum (bootDevice) + + # Calling swriteboot. The first argument is the disk to write + # to and the second argument is a path to the bootstrap loader + # file. + args = ("swriteboot", ("/dev/%s" % wbd), "/boot/bootlx") + rhpl.executil.execWithRedirect ('/sbin/swriteboot', args, + root = instRoot, + stdout = "/dev/tty5", + stderr = "/dev/tty5") + + # Calling abootconf to configure the installed aboot. The + # first argument is the disk to use, the second argument is + # the number of the partition on which aboot.conf resides. + # It's always the boot partition whether it's / or /boot (with + # the mount point being omitted.) + args = ("abootconf", ("/dev/%s" % wbd), str (bdpn)) + rhpl.executil.execWithRedirect ('/sbin/abootconf', args, + root = instRoot, + stdout = "/dev/tty5", + stderr = "/dev/tty5") + + + def write(self, instRoot, fsset, bl, kernelList, chainList, + defaultDev, justConfig, intf): + if len(kernelList) < 1: + self.noKernelsWarn(intf) + + self.writeAboot(instRoot, fsset, bl, kernelList, + chainList, defaultDev, justConfig) + + def __init__(self): + bootloaderInfo.__init__(self) + self.useGrubVal = 0 + self.configfile = "/etc/aboot.conf" + # self.kernelLocation is already set to what we need. + self.password = None + self.pure = None diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 14d33835a..b33c11885 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -78,6 +78,27 @@ def syncDataToDisk(dev, mntpt, instRoot = "/"): stderr = "/dev/tty5", root = instRoot) +def rootIsDevice(dev): + if dev.startswith("LABEL=") or dev.startswith("UUID="): + return False + return True + +# hackery to determine if we should do root=LABEL=/ or whatnot +# as usual, knows too much about anaconda +def getRootDevName(initrd, fsset, rootDev, instRoot): + if not os.access(instRoot + initrd, os.R_OK): + return "/dev/%s" % (rootDev,) + + try: + rootEntry = fsset.getEntryByMountPoint("/") + if rootEntry.getUuid() is not None: + return "UUID=%s" %(rootEntry.getUuid(),) + elif rootEntry.getLabel() is not None and rootEntry.device.doLabel is not None: + return "LABEL=%s" %(rootEntry.getLabel(),) + return "/dev/%s" %(rootDev,) + except: + return "/dev/%s" %(rootDev,) + class BootyNoKernelWarning: def __init__ (self, value=""): self.value = value @@ -564,555 +585,6 @@ class bootloaderInfo: con = flags.virtpconsole self.args.append("console=%s" %(con,)) - -class grubBootloaderInfo(bootloaderInfo): - def setPassword(self, val, isCrypted = 1): - if not val: - self.password = val - self.pure = val - return - - if isCrypted and self.useGrubVal == 0: - self.pure = None - return - elif isCrypted: - self.password = val - self.pure = None - else: - salt = "$1$" - saltLen = 8 - - saltchars = string.letters + string.digits + './' - for i in range(saltLen): - salt += random.choice(saltchars) - - self.password = crypt.crypt(val, salt) - self.pure = val - - def getPassword (self): - return self.pure - - def setForceLBA(self, val): - self.forceLBA32 = val - - def setUseGrub(self, val): - self.useGrubVal = val - - def getPhysicalDevices(self, device): - # This finds a list of devices on which the given device name resides. - # Accepted values for "device" are raid1 md devices (i.e. "md0"), - # physical disks ("hda"), and real partitions on physical disks - # ("hda1"). Volume groups/logical volumes are not accepted. - # - # XXX this has internal anaconda-ish knowledge. ick. - import isys - import lvm - - if string.split(device, '/', 1)[0] in map (lambda vg: vg[0], - lvm.vglist()): - return [] - - if device.startswith("mapper/luks-"): - return [] - - if device.startswith('md'): - bootable = 0 - parts = checkbootloader.getRaidDisks(device, 1, stripPart=0) - parts.sort() - return parts - - return [device] - - def runGrubInstall(self, instRoot, bootDev, cmds, cfPath): - if cfPath == "/": - syncDataToDisk(bootDev, "/boot", instRoot) - else: - syncDataToDisk(bootDev, "/", instRoot) - - # copy the stage files over into /boot - rhpl.executil.execWithRedirect( "/sbin/grub-install", - ["/sbin/grub-install", "--just-copy"], - stdout = "/dev/tty5", stderr = "/dev/tty5", - root = instRoot) - - # really install the bootloader - for cmd in cmds: - p = os.pipe() - os.write(p[1], cmd + '\n') - os.close(p[1]) - import time - - # FIXME: hack to try to make sure everything is written - # to the disk - if cfPath == "/": - syncDataToDisk(bootDev, "/boot", instRoot) - else: - syncDataToDisk(bootDev, "/", instRoot) - - rhpl.executil.execWithRedirect('/sbin/grub' , - [ "grub", "--batch", "--no-floppy", - "--device-map=/boot/grub/device.map" ], - stdin = p[0], - stdout = "/dev/tty5", stderr = "/dev/tty5", - root = instRoot) - os.close(p[0]) - - def installGrub(self, instRoot, bootDevs, grubTarget, grubPath, fsset, - target, cfPath): - args = "--stage2=/boot/grub/stage2 " - if self.forceLBA32: - args = "%s--force-lba " % (args,) - - cmds = [] - for bootDev in bootDevs: - gtPart = self.getMatchingPart(bootDev, grubTarget) - gtDisk = self.grubbyPartitionName(getDiskPart(gtPart)[0]) - bPart = self.grubbyPartitionName(bootDev) - cmd = "root %s\n" % (bPart,) - - stage1Target = gtDisk - if target == "partition": - stage1Target = self.grubbyPartitionName(gtPart) - - cmd += "install %s%s/stage1 d %s %s/stage2 p %s%s/grub.conf" % \ - (args, grubPath, stage1Target, grubPath, bPart, grubPath) - cmds.append(cmd) - - self.runGrubInstall(instRoot, bootDev, cmds, cfPath) - - def writeGrub(self, instRoot, fsset, bl, kernelList, chainList, - defaultDev, justConfigFile): - - images = bl.images.getImages() - rootDev = fsset.getEntryByMountPoint("/").device.getDevice() - - # XXX old config file should be read here for upgrade - - cf = "%s%s" % (instRoot, self.configfile) - self.perms = 0600 - if os.access (cf, os.R_OK): - self.perms = os.stat(cf)[0] & 0777 - os.rename(cf, cf + '.rpmsave') - - grubTarget = bl.getDevice() - target = "mbr" - if (grubTarget.startswith('rd/') or grubTarget.startswith('ida/') or - grubTarget.startswith('cciss/') or - grubTarget.startswith('sx8/') or - grubTarget.startswith('mapper/')): - if grubTarget[-1].isdigit(): - if grubTarget[-2] == 'p' or \ - (grubTarget[-2].isdigit() and grubTarget[-3] == 'p'): - target = "partition" - elif grubTarget[-1].isdigit() and not grubTarget.startswith('md'): - target = "partition" - - f = open(cf, "w+") - - f.write("# grub.conf generated by anaconda\n") - f.write("#\n") - f.write("# Note that you do not have to rerun grub " - "after making changes to this file\n") - - bootDev = fsset.getEntryByMountPoint("/boot") - grubPath = "/grub" - cfPath = "/" - if not bootDev: - bootDev = fsset.getEntryByMountPoint("/") - grubPath = "/boot/grub" - cfPath = "/boot/" - f.write("# NOTICE: You do not have a /boot partition. " - "This means that\n") - f.write("# all kernel and initrd paths are relative " - "to /, eg.\n") - else: - f.write("# NOTICE: You have a /boot partition. This means " - "that\n") - f.write("# all kernel and initrd paths are relative " - "to /boot/, eg.\n") - - bootDevs = self.getPhysicalDevices(bootDev.device.getDevice()) - bootDev = bootDev.device.getDevice() - - f.write('# root %s\n' % self.grubbyPartitionName(bootDevs[0])) - f.write("# kernel %svmlinuz-version ro " - "root=/dev/%s\n" % (cfPath, rootDev)) - f.write("# initrd %sinitrd-version.img\n" % (cfPath)) - f.write("#boot=/dev/%s\n" % (grubTarget)) - - # get the default image to boot... we have to walk and find it - # since grub indexes by where it is in the config file - if defaultDev == rootDev: - default = 0 - else: - # if the default isn't linux, it's the first thing in the - # chain list - default = len(kernelList) - - # keep track of which devices are used for the device.map - usedDevs = {} - - f.write('default=%s\n' % (default)) - f.write('timeout=%d\n' % (self.timeout or 0)) - - if self.serial == 1: - # grub the 0-based number of the serial console device - unit = self.serialDevice[-1] - - # and we want to set the speed too - speedend = 0 - for char in self.serialOptions: - if char not in string.digits: - break - speedend = speedend + 1 - if speedend != 0: - speed = self.serialOptions[:speedend] - else: - # reasonable default - speed = "9600" - - f.write("serial --unit=%s --speed=%s\n" %(unit, speed)) - f.write("terminal --timeout=%s serial console\n" % (self.timeout or 5)) - else: - # we only want splashimage if they're not using a serial console - if os.access("%s/boot/grub/splash.xpm.gz" %(instRoot,), os.R_OK): - f.write('splashimage=%s%sgrub/splash.xpm.gz\n' - % (self.grubbyPartitionName(bootDevs[0]), cfPath)) - f.write("hiddenmenu\n") - - for dev in self.getPhysicalDevices(grubTarget): - usedDevs[dev] = 1 - - if self.password: - f.write('password --md5 %s\n' %(self.password)) - - for (label, longlabel, version) in kernelList: - kernelTag = "-" + version - kernelFile = "%svmlinuz%s" % (cfPath, kernelTag) - - initrd = self.makeInitrd(kernelTag) - - f.write('title %s (%s)\n' % (longlabel, version)) - f.write('\troot %s\n' % self.grubbyPartitionName(bootDevs[0])) - - realroot = getRootDevName(initrd, fsset, rootDev, instRoot) - realroot = " root=%s" %(realroot,) - - if version.endswith("xen0") or (version.endswith("xen") and not os.path.exists("/proc/xen")): - # hypervisor case - sermap = { "ttyS0": "com1", "ttyS1": "com2", - "ttyS2": "com3", "ttyS3": "com4" } - if self.serial and sermap.has_key(self.serialDevice) and \ - self.serialOptions: - hvs = "%s=%s" %(sermap[self.serialDevice], - self.serialOptions) - else: - hvs = "" - if version.endswith("xen0"): - hvFile = "%sxen.gz-%s %s" %(cfPath, - version.replace("xen0", ""), - hvs) - else: - hvFile = "%sxen.gz-%s %s" %(cfPath, - version.replace("xen", ""), - hvs) - f.write('\tkernel %s\n' %(hvFile,)) - f.write('\tmodule %s ro%s' %(kernelFile, realroot)) - if self.args.get(): - f.write(' %s' % self.args.get()) - f.write('\n') - - if os.access (instRoot + initrd, os.R_OK): - f.write('\tmodule %sinitrd%s.img\n' % (cfPath, kernelTag)) - else: # normal kernel - f.write('\tkernel %s ro%s' % (kernelFile, realroot)) - if self.args.get(): - f.write(' %s' % self.args.get()) - f.write('\n') - - if os.access (instRoot + initrd, os.R_OK): - f.write('\tinitrd %sinitrd%s.img\n' % (cfPath, kernelTag)) - - for (label, longlabel, device) in chainList: - if ((not longlabel) or (longlabel == "")): - continue - f.write('title %s\n' % (longlabel)) - f.write('\trootnoverify %s\n' % self.grubbyPartitionName(device)) -# f.write('\tmakeactive\n') - f.write('\tchainloader +1') - f.write('\n') - usedDevs[device] = 1 - - f.close() - - if not "/efi/" in cf: - os.chmod(cf, self.perms) - - try: - # make symlink for menu.lst (default config file name) - menulst = "%s%s/menu.lst" % (instRoot, self.configdir) - if os.access (menulst, os.R_OK): - os.rename(menulst, menulst + ".rpmsave") - os.symlink("./grub.conf", menulst) - except: - pass - - try: - # make symlink for /etc/grub.conf (config files belong in /etc) - etcgrub = "%s%s" % (instRoot, "/etc/grub.conf") - if os.access (etcgrub, os.R_OK): - os.rename(etcgrub, etcgrub + ".rpmsave") - os.symlink(".." + self.configfile, etcgrub) - except: - pass - - for dev in self.getPhysicalDevices(rootDev) + bootDevs: - usedDevs[dev] = 1 - - if os.access(instRoot + "/boot/grub/device.map", os.R_OK): - os.rename(instRoot + "/boot/grub/device.map", - instRoot + "/boot/grub/device.map.rpmsave") - if 1: # not os.access(instRoot + "/boot/grub/device.map", os.R_OK): - f = open(instRoot + "/boot/grub/device.map", "w+") - f.write("# this device map was generated by anaconda\n") - devs = usedDevs.keys() - usedDevs = {} - for dev in devs: - drive = getDiskPart(dev)[0] - if usedDevs.has_key(drive): - continue - usedDevs[drive] = 1 - devs = usedDevs.keys() - devs.sort() - for drive in devs: - # XXX hack city. If they're not the sort of thing that'll - # be in the device map, they shouldn't still be in the list. - if not drive.startswith('md'): - f.write("(%s) /dev/%s\n" % (self.grubbyDiskName(drive), - drive)) - f.close() - - sysconf = '/etc/sysconfig/grub' - if os.access (instRoot + sysconf, os.R_OK): - self.perms = os.stat(instRoot + sysconf)[0] & 0777 - os.rename(instRoot + sysconf, - instRoot + sysconf + '.rpmsave') - # if it's an absolute symlink, just get it out of our way - elif (os.path.islink(instRoot + sysconf) and - os.readlink(instRoot + sysconf)[0] == '/'): - os.rename(instRoot + sysconf, - instRoot + sysconf + '.rpmsave') - f = open(instRoot + sysconf, 'w+') - f.write("boot=/dev/%s\n" %(grubTarget,)) - # XXX forcelba never gets read back... - if self.forceLBA32: - f.write("forcelba=1\n") - else: - f.write("forcelba=0\n") - f.close() - - if not justConfigFile: - self.installGrub(instRoot, bootDevs, grubTarget, grubPath, fsset, \ - target, cfPath) - - return "" - - def getMatchingPart(self, bootDev, target): - bootName, bootPartNum = getDiskPart(bootDev) - devices = self.getPhysicalDevices(target) - for device in devices: - name, partNum = getDiskPart(device) - if name == bootName: - return device - return devices[0] - - def grubbyDiskName(self, name): - return "hd%d" % self.drivelist.index(name) - - def grubbyPartitionName(self, dev): - (name, partNum) = getDiskPart(dev) - if partNum != None: - return "(%s,%d)" % (self.grubbyDiskName(name), partNum) - else: - return "(%s)" %(self.grubbyDiskName(name)) - - - def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, - chainList, defaultDev): - config = bootloaderInfo.getBootloaderConfig(self, instRoot, fsset, - bl, kernelList, chainList, - defaultDev) - - liloTarget = bl.getDevice() - - config.addEntry("boot", '/dev/' + liloTarget, replace = 0) - config.addEntry("map", "/boot/map", replace = 0) - config.addEntry("install", "/boot/boot.b", replace = 0) - message = "/boot/message" - - if self.pure is not None and not self.useGrubVal: - config.addEntry("restricted", replace = 0) - config.addEntry("password", self.pure, replace = 0) - - if self.serial == 1: - # grab the 0-based number of the serial console device - unit = self.serialDevice[-1] - # FIXME: we should probably put some options, but lilo - # only supports up to 9600 baud so just use the defaults - # it's better than nothing :( - config.addEntry("serial=%s" %(unit,)) - else: - # message screws up serial console - if os.access(instRoot + message, os.R_OK): - config.addEntry("message", message, replace = 0) - - if not config.testEntry('lba32'): - if self.forceLBA32 or (bl.above1024 and - rhpl.getArch() != "x86_64"): - config.addEntry("lba32", replace = 0) - - return config - - # this is a hackish function that depends on the way anaconda writes - # out the grub.conf with a #boot= comment - # XXX this falls into the category of self.doUpgradeOnly - def upgradeGrub(self, instRoot, fsset, bl, kernelList, chainList, - defaultDev, justConfigFile): - if justConfigFile: - return "" - - theDev = None - for (fn, stanza) in [ ("/etc/sysconfig/grub", "boot="), - ("/boot/grub/grub.conf", "#boot=") ]: - try: - f = open(instRoot + fn, "r") - except: - continue - - # the following bits of code are straight from checkbootloader.py - lines = f.readlines() - f.close() - for line in lines: - if line.startswith(stanza): - theDev = checkbootloader.getBootDevString(line) - break - if theDev is not None: - break - - if theDev is None: - # we could find the dev before, but can't now... cry about it - return "" - - # migrate info to /etc/sysconfig/grub - self.writeSysconfig(instRoot, theDev) - - # more suckage. grub-install can't work without a valid /etc/mtab - # so we have to do shenanigans to get updated grub installed... - # steal some more code above - bootDev = fsset.getEntryByMountPoint("/boot") - grubPath = "/grub" - cfPath = "/" - if not bootDev: - bootDev = fsset.getEntryByMountPoint("/") - grubPath = "/boot/grub" - cfPath = "/boot/" - - masterBootDev = bootDev.device.getDevice(asBoot = 0) - if masterBootDev[0:2] == 'md': - rootDevs = checkbootloader.getRaidDisks(masterBootDev, raidLevel=1, - stripPart = 0) - else: - rootDevs = [masterBootDev] - - if theDev[5:7] == 'md': - stage1Devs = checkbootloader.getRaidDisks(theDev[5:], raidLevel=1) - else: - stage1Devs = [theDev[5:]] - - for stage1Dev in stage1Devs: - # cross fingers; if we can't find a root device on the same - # hardware as this boot device, we just blindly hope the first - # thing in the list works. - - grubbyStage1Dev = self.grubbyPartitionName(stage1Dev) - - grubbyRootPart = self.grubbyPartitionName(rootDevs[0]) - - for rootDev in rootDevs: - testGrubbyRootDev = getDiskPart(rootDev)[0] - testGrubbyRootDev = self.grubbyPartitionName(testGrubbyRootDev) - - if grubbyStage1Dev == testGrubbyRootDev: - grubbyRootPart = self.grubbyPartitionName(rootDev) - break - - args = "--stage2=/boot/grub/stage2 " - cmd ="root %s" % (grubbyRootPart,) - cmds = [ cmd ] - cmd = "install %s%s/stage1 d %s %s/stage2 p %s%s/grub.conf" \ - % (args, grubPath, grubbyStage1Dev, grubPath, grubbyRootPart, - grubPath) - cmds.append(cmd) - - if not justConfigFile: - self.runGrubInstall(instRoot, bootDev.device.setupDevice(), - cmds, cfPath) - - return "" - - def writeSysconfig(self, instRoot, installDev): - sysconf = '/etc/sysconfig/grub' - if not os.access(instRoot + sysconf, os.R_OK): - f = open(instRoot + sysconf, "w+") - f.write("boot=%s\n" %(installDev,)) - # XXX forcelba never gets read back at all... - if self.forceLBA32: - f.write("forcelba=1\n") - else: - f.write("forcelba=0\n") - f.close() - - def write(self, instRoot, fsset, bl, kernelList, chainList, - defaultDev, justConfig, intf): - if self.timeout is None and chainList: - self.timeout = 5 - - # XXX HACK ALERT - see declaration above - if self.doUpgradeOnly: - if self.useGrubVal: - self.upgradeGrub(instRoot, fsset, bl, kernelList, - chainList, defaultDev, justConfig) - return - - if len(kernelList) < 1: - self.noKernelsWarn(intf) - - out = self.writeGrub(instRoot, fsset, bl, kernelList, - chainList, defaultDev, - justConfig | (not self.useGrubVal)) - - - def getArgList(self): - args = bootloaderInfo.getArgList(self) - - if self.forceLBA32: - args.append("--lba32") - if self.password: - args.append("--md5pass=%s" %(self.password)) - - return args - - def __init__(self): - bootloaderInfo.__init__(self) - self._configdir = "/boot/grub" - self._configname = "grub.conf" - # XXX use checkbootloader to determine what to default to - self.useGrubVal = 1 - self.kernelLocation = "/boot/" - self.password = None - self.pure = None - - class efiBootloaderInfo(bootloaderInfo): def getBootloaderName(self): return self._bootloader @@ -1189,697 +661,3 @@ class efiBootloaderInfo(bootloaderInfo): self._bootloader = "grub.efi" self.useGrubVal = 1 self.kernelLocation = "" - -class x86BootloaderInfo(grubBootloaderInfo, efiBootloaderInfo): - def write(self, instRoot, fsset, bl, kernelList, chainList, - defaultDev, justConfig, intf): - grubBootloaderInfo.write(self, instRoot, fsset, bl, kernelList, - chainList, defaultDev, justConfig, intf) - - # XXX move the lilo.conf out of the way if they're using GRUB - # so that /sbin/installkernel does a more correct thing - if self.useGrubVal and os.access(instRoot + '/etc/lilo.conf', os.R_OK): - os.rename(instRoot + "/etc/lilo.conf", - instRoot + "/etc/lilo.conf.anaconda") - - def installGrub(self, *args): - args = [self] + list(args) - try: - apply(efiBootloaderInfo.installGrub, args, {}) - except EnvironmentError: - apply(grubBootloaderInfo.installGrub, args, {}) - - def __init__(self): - grubBootloaderInfo.__init__(self) - efiBootloaderInfo.__init__(self, initialize=False) - -class ia64BootloaderInfo(efiBootloaderInfo): - def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, - chainList, defaultDev): - config = bootloaderInfo.getBootloaderConfig(self, instRoot, fsset, - bl, kernelList, chainList, - defaultDev) - # altix boxes need relocatable (#120851) - config.addEntry("relocatable") - - return config - - def writeLilo(self, instRoot, fsset, bl, kernelList, - chainList, defaultDev, justConfig): - config = self.getBootloaderConfig(instRoot, fsset, bl, - kernelList, chainList, defaultDev) - config.write(instRoot + self.configfile, perms = 0755) - - return "" - - def write(self, instRoot, fsset, bl, kernelList, chainList, - defaultDev, justConfig, intf): - if len(kernelList) >= 1: - out = self.writeLilo(instRoot, fsset, bl, kernelList, - chainList, defaultDev, justConfig) - else: - self.noKernelsWarn(intf) - - self.removeOldEfiEntries(instRoot) - self.addNewEfiEntry(instRoot, fsset) - - def makeInitrd(self, kernelTag): - return "/boot/efi/EFI/redhat/initrd%s.img" % kernelTag - - def __init__(self): - efiBootloaderInfo.__init__(self) - self._configname = "elilo.conf" - self._bootloader = "elilo.efi" - -class s390BootloaderInfo(bootloaderInfo): - def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, - chainList, defaultDev): - images = bl.images.getImages() - - # on upgrade read in the lilo config file - lilo = LiloConfigFile () - self.perms = 0600 - if os.access (instRoot + self.configfile, os.R_OK): - self.perms = os.stat(instRoot + self.configfile)[0] & 0777 - lilo.read (instRoot + self.configfile) - os.rename(instRoot + self.configfile, - instRoot + self.configfile + '.rpmsave') - - # Remove any invalid entries that are in the file; we probably - # just removed those kernels. - for label in lilo.listImages(): - (fsType, sl, path, other) = lilo.getImage(label) - if fsType == "other": continue - - if not os.access(instRoot + sl.getPath(), os.R_OK): - lilo.delImage(label) - - rootDev = fsset.getEntryByMountPoint("/").device.getDevice() - if not rootDev: - raise RuntimeError, "Installing zipl, but there is no root device" - - if rootDev == defaultDev: - lilo.addEntry("default", kernelList[0][0]) - else: - lilo.addEntry("default", chainList[0][0]) - - for (label, longlabel, version) in kernelList: - kernelTag = "-" + version - kernelFile = self.kernelLocation + "vmlinuz" + kernelTag - - try: - lilo.delImage(label) - except IndexError, msg: - pass - - sl = LiloConfigFile(imageType = "image", path = kernelFile) - - initrd = self.makeInitrd(kernelTag) - - sl.addEntry("label", label) - if os.access (instRoot + initrd, os.R_OK): - sl.addEntry("initrd", - "%sinitrd%s.img" %(self.kernelLocation, kernelTag)) - - sl.addEntry("read-only") - sl.addEntry("root", '/dev/' + rootDev) - sl.addEntry("ipldevice", '/dev/' + rootDev[:-1]) - - if self.args.get(): - sl.addEntry('append', '"%s"' % self.args.get()) - - lilo.addImage (sl) - - for (label, longlabel, device) in chainList: - if ((not label) or (label == "")): - continue - try: - (fsType, sl, path, other) = lilo.getImage(label) - lilo.delImage(label) - except IndexError: - sl = LiloConfigFile(imageType = "other", - path = "/dev/%s" %(device)) - sl.addEntry("optional") - - sl.addEntry("label", label) - lilo.addImage (sl) - - # Sanity check #1. There could be aliases in sections which conflict - # with the new images we just created. If so, erase those aliases - imageNames = {} - for label in lilo.listImages(): - imageNames[label] = 1 - - for label in lilo.listImages(): - (fsType, sl, path, other) = lilo.getImage(label) - if sl.testEntry('alias'): - alias = sl.getEntry('alias') - if imageNames.has_key(alias): - sl.delEntry('alias') - imageNames[alias] = 1 - - # Sanity check #2. If single-key is turned on, go through all of - # the image names (including aliases) (we just built the list) and - # see if single-key will still work. - if lilo.testEntry('single-key'): - singleKeys = {} - turnOff = 0 - for label in imageNames.keys(): - l = label[0] - if singleKeys.has_key(l): - turnOff = 1 - singleKeys[l] = 1 - if turnOff: - lilo.delEntry('single-key') - - return lilo - - def writeChandevConf(self, bl, instroot): # S/390 only - cf = "/etc/chandev.conf" - self.perms = 0644 - if bl.args.chandevget(): - fd = os.open(instroot + "/etc/chandev.conf", - os.O_WRONLY | os.O_CREAT) - os.write(fd, "noauto\n") - for cdev in bl.args.chandevget(): - os.write(fd,'%s\n' % cdev) - os.close(fd) - return "" - - - def writeZipl(self, instRoot, fsset, bl, kernelList, chainList, - defaultDev, justConfigFile): - images = bl.images.getImages() - rootDev = fsset.getEntryByMountPoint("/").device.getDevice() - - cf = '/etc/zipl.conf' - self.perms = 0600 - if os.access (instRoot + cf, os.R_OK): - self.perms = os.stat(instRoot + cf)[0] & 0777 - os.rename(instRoot + cf, - instRoot + cf + '.rpmsave') - - f = open(instRoot + cf, "w+") - - f.write('[defaultboot]\n') - f.write('default=' + kernelList[0][0] + '\n') - f.write('target=%s\n' % (self.kernelLocation)) - - cfPath = "/boot/" - for (label, longlabel, version) in kernelList: - kernelTag = "-" + version - kernelFile = "%svmlinuz%s" % (cfPath, kernelTag) - - initrd = self.makeInitrd(kernelTag) - f.write('[%s]\n' % (label)) - f.write('\timage=%s\n' % (kernelFile)) - if os.access (instRoot + initrd, os.R_OK): - f.write('\tramdisk=%sinitrd%s.img\n' %(self.kernelLocation, - kernelTag)) - realroot = getRootDevName(initrd, fsset, rootDev, instRoot) - f.write('\tparameters="root=%s' %(realroot,)) - if bl.args.get(): - f.write(' %s' % (bl.args.get())) - f.write('"\n') - - f.close() - - if not justConfigFile: - argv = [ "/sbin/zipl" ] - rhpl.executil.execWithRedirect(argv[0], argv, root = instRoot, - stdout = "/dev/stdout", - stderr = "/dev/stderr") - - return "" - - def write(self, instRoot, fsset, bl, kernelList, chainList, - defaultDev, justConfig, intf): - out = self.writeZipl(instRoot, fsset, bl, kernelList, - chainList, defaultDev, - justConfig | (not self.useZiplVal)) - out = self.writeChandevConf(bl, instRoot) - - def __init__(self): - bootloaderInfo.__init__(self) - self.useZiplVal = 1 # only used on s390 - self.kernelLocation = "/boot/" - self.configfile = "/etc/zipl.conf" - - -class alphaBootloaderInfo(bootloaderInfo): - def wholeDevice (self, path): - (device, foo) = getDiskPart(path) - return device - - def partitionNum (self, path): - # getDiskPart returns part numbers 0-based; we need it one based - # *sigh* - (foo, partitionNumber) = getDiskPart(path) - return partitionNumber + 1 - - def writeAboot(self, instRoot, fsset, bl, kernelList, - chainList, defaultDev, justConfig): - # Get bootDevice and rootDevice - rootDevice = fsset.getEntryByMountPoint("/").device.getDevice() - if fsset.getEntryByMountPoint("/boot"): - bootDevice = fsset.getEntryByMountPoint("/boot").device.getDevice() - else: - bootDevice = rootDevice - bootnotroot = bootDevice != rootDevice - - # If /etc/aboot.conf already exists we rename it - # /etc/aboot.conf.rpmsave. - if os.path.isfile(instRoot + self.configfile): - os.rename (instRoot + self.configfile, - instRoot + self.configfile + ".rpmsave") - - # Then we create the necessary files. If the root device isn't - # the boot device, we create /boot/etc/ where the aboot.conf - # will live, and we create /etc/aboot.conf as a symlink to it. - if bootnotroot: - # Do we have /boot/etc ? If not, create one - if not os.path.isdir (instRoot + '/boot/etc'): - os.mkdir(instRoot + '/boot/etc', 0755) - - # We install the symlink (/etc/aboot.conf has already been - # renamed in necessary.) - os.symlink("../boot" + self.configfile, instRoot + self.configfile) - - cfPath = instRoot + "/boot" + self.configfile - # Kernel path is set to / because a boot partition will - # be a root on its own. - kernelPath = '/' - # Otherwise, we just need to create /etc/aboot.conf. - else: - cfPath = instRoot + self.configfile - kernelPath = self.kernelLocation - - # If we already have an aboot.conf, rename it - if os.access (cfPath, os.R_OK): - self.perms = os.stat(cfPath)[0] & 0777 - os.rename(cfPath, cfPath + '.rpmsave') - - # Now we're going to create and populate cfPath. - f = open (cfPath, 'w+') - f.write ("# aboot default configurations\n") - - if bootnotroot: - f.write ("# NOTICE: You have a /boot partition. This means that\n") - f.write ("# all kernel paths are relative to /boot/\n") - - # bpn is the boot partition number. - bpn = self.partitionNum(bootDevice) - lines = 0 - - # We write entries line using the following format: - # root= [options] - # We get all the kernels we need to know about in kernelList. - - for (kernel, tag, version) in kernelList: - kernelTag = "-" + version - kernelFile = "%svmlinuz%s" %(kernelPath, kernelTag) - - f.write("%d:%d%s" %(lines, bpn, kernelFile)) - - # See if we can come up with an initrd argument that exists - initrd = self.makeInitrd(kernelTag) - if os.path.isfile(instRoot + initrd): - f.write(" initrd=%sinitrd%s.img" %(kernelPath, kernelTag)) - - realroot = getRootDevName(initrd, fsset, rootDevice, instRoot) - f.write(" root=%s" %(realroot,)) - - args = self.args.get() - if args: - f.write(" %s" %(args,)) - - f.write("\n") - lines = lines + 1 - - # We're done writing the file - f.close () - del f - - if not justConfig: - # Now we're ready to write the relevant boot information. wbd - # is the whole boot device, bdpn is the boot device partition - # number. - wbd = self.wholeDevice (bootDevice) - bdpn = self.partitionNum (bootDevice) - - # Calling swriteboot. The first argument is the disk to write - # to and the second argument is a path to the bootstrap loader - # file. - args = ("swriteboot", ("/dev/%s" % wbd), "/boot/bootlx") - rhpl.executil.execWithRedirect ('/sbin/swriteboot', args, - root = instRoot, - stdout = "/dev/tty5", - stderr = "/dev/tty5") - - # Calling abootconf to configure the installed aboot. The - # first argument is the disk to use, the second argument is - # the number of the partition on which aboot.conf resides. - # It's always the boot partition whether it's / or /boot (with - # the mount point being omitted.) - args = ("abootconf", ("/dev/%s" % wbd), str (bdpn)) - rhpl.executil.execWithRedirect ('/sbin/abootconf', args, - root = instRoot, - stdout = "/dev/tty5", - stderr = "/dev/tty5") - - - def write(self, instRoot, fsset, bl, kernelList, chainList, - defaultDev, justConfig, intf): - if len(kernelList) < 1: - self.noKernelsWarn(intf) - - self.writeAboot(instRoot, fsset, bl, kernelList, - chainList, defaultDev, justConfig) - - def __init__(self): - bootloaderInfo.__init__(self) - self.useGrubVal = 0 - self.configfile = "/etc/aboot.conf" - # self.kernelLocation is already set to what we need. - self.password = None - self.pure = None - - -class ppcBootloaderInfo(bootloaderInfo): - def getBootDevs(self, fs, bl): - import fsset - - devs = [] - machine = rhpl.getPPCMachine() - - if machine == 'pSeries': - for entry in fs.entries: - if isinstance(entry.fsystem, fsset.prepbootFileSystem) \ - and entry.format: - devs.append('/dev/%s' % (entry.device.getDevice(),)) - elif machine == 'PMac': - for entry in fs.entries: - if isinstance(entry.fsystem, fsset.applebootstrapFileSystem) \ - and entry.format: - devs.append('/dev/%s' % (entry.device.getDevice(),)) - - if len(devs) == 0: - # Try to get a boot device; bplan OF understands ext3 - if machine == 'Pegasos' or machine == 'Efika': - entry = fs.getEntryByMountPoint('/boot') - # Try / if we don't have this we're not going to work - if not entry: - entry = fs.getEntryByMountPoint('/') - if entry: - dev = "/dev/%s" % (entry.device.getDevice(asBoot=1),) - devs.append(dev) - else: - if bl.getDevice(): - devs.append("/dev/%s" % bl.getDevice()) - return devs - - - def writeYaboot(self, instRoot, fsset, bl, kernelList, - chainList, defaultDev, justConfigFile): - - yabootTarget = string.join(self.getBootDevs(fsset, bl)) - - bootDev = fsset.getEntryByMountPoint("/boot") - if bootDev: - cf = "/boot/etc/yaboot.conf" - cfPath = "" - if not os.path.isdir(instRoot + "/boot/etc"): - os.mkdir(instRoot + "/boot/etc") - else: - bootDev = fsset.getEntryByMountPoint("/") - cfPath = "/boot" - cf = "/etc/yaboot.conf" - bootDev = bootDev.device.getDevice(asBoot = 1) - - f = open(instRoot + cf, "w+") - - f.write("# yaboot.conf generated by anaconda\n\n") - - f.write("boot=%s\n" %(yabootTarget,)) - f.write("init-message=\"Welcome to %s!\\nHit for boot options\"\n\n" - % productName) - - (name, partNum) = getDiskPart(bootDev) - partno = partNum + 1 # 1 based - - f.write("partition=%s\n" %(partno,)) - - f.write("timeout=%s\n" % (self.timeout or 80)) - f.write("install=/usr/lib/yaboot/yaboot\n") - f.write("delay=5\n") - f.write("enablecdboot\n") - f.write("enableofboot\n") - f.write("enablenetboot\n") - - yabootProg = "/sbin/mkofboot" - if rhpl.getPPCMachine() == "PMac": - # write out the first hfs/hfs+ partition as being macosx - for (label, longlabel, device) in chainList: - if ((not label) or (label == "")): - continue - f.write("macosx=/dev/%s\n" %(device,)) - break - - f.write("magicboot=/usr/lib/yaboot/ofboot\n") - - elif rhpl.getPPCMachine() == "pSeries": - f.write("nonvram\n") - f.write("fstype=raw\n") - - else: # Default non-destructive case for anything else. - f.write("nonvram\n") - f.write("mntpoint=/boot/yaboot\n") - f.write("usemount\n") - if not os.access(instRoot + "/boot/yaboot", os.R_OK): - os.mkdir(instRoot + "/boot/yaboot") - yabootProg = "/sbin/ybin" - - if self.password: - f.write("password=%s\n" %(self.password,)) - f.write("restricted\n") - - f.write("\n") - - rootDev = fsset.getEntryByMountPoint("/").device.getDevice() - - for (label, longlabel, version) in kernelList: - kernelTag = "-" + version - kernelFile = "%s/vmlinuz%s" %(cfPath, kernelTag) - - f.write("image=%s\n" %(kernelFile,)) - f.write("\tlabel=%s\n" %(label,)) - f.write("\tread-only\n") - - initrd = self.makeInitrd(kernelTag) - if os.access(instRoot + initrd, os.R_OK): - f.write("\tinitrd=%s/initrd%s.img\n" %(cfPath,kernelTag)) - - append = "%s" %(self.args.get(),) - - realroot = getRootDevName(initrd, fsset, rootDev, instRoot) - if rootIsDevice(realroot): - f.write("\troot=%s\n" %(realroot,)) - else: - if len(append) > 0: - append = "%s root=%s" %(append,realroot) - else: - append = "root=%s" %(realroot,) - - if len(append) > 0: - f.write("\tappend=\"%s\"\n" %(append,)) - f.write("\n") - - f.close() - os.chmod(instRoot + cf, 0600) - - # FIXME: hack to make sure things are written to disk - import isys - isys.sync() - isys.sync() - isys.sync() - - ybinargs = [ yabootProg, "-f", "-C", cf ] - - if not flags.test: - rhpl.executil.execWithRedirect(ybinargs[0], - ybinargs, - stdout = "/dev/tty5", - stderr = "/dev/tty5", - root = instRoot) - - if (not os.access(instRoot + "/etc/yaboot.conf", os.R_OK) and - os.access(instRoot + "/boot/etc/yaboot.conf", os.R_OK)): - os.symlink("../boot/etc/yaboot.conf", - instRoot + "/etc/yaboot.conf") - - return "" - - def setPassword(self, val, isCrypted = 1): - # yaboot just handles the password and doesn't care if its crypted - # or not - self.password = val - - def write(self, instRoot, fsset, bl, kernelList, chainList, - defaultDev, justConfig, intf): - if len(kernelList) >= 1: - out = self.writeYaboot(instRoot, fsset, bl, kernelList, - chainList, defaultDev, justConfig) - else: - self.noKernelsWarn(intf) - - def __init__(self): - bootloaderInfo.__init__(self) - self.useYabootVal = 1 - self.kernelLocation = "/boot" - self.configfile = "/etc/yaboot.conf" - -class sparcBootloaderInfo(bootloaderInfo): - def writeSilo(self, instRoot, fsset, bl, kernelList, - chainList, defaultDev, justConfigFile): - - bootDev = fsset.getEntryByMountPoint("/boot") - mf = '/silo.message' - if bootDev: - cf = "/boot/silo.conf" - mfdir = '/boot' - cfPath = "" - if not os.path.isdir(instRoot + "/boot"): - os.mkdir(instRoot + "/boot") - else: - bootDev = fsset.getEntryByMountPoint("/") - cf = "/etc/silo.conf" - mfdir = '/etc' - cfPath = "/boot" - bootDev = bootDev.device.getDevice(asBoot = 1) - - f = open(instRoot + mfdir + mf, "w+") - f.write("Welcome to %s!\nHit for boot options\n\n" % productName) - f.close() - os.chmod(instRoot + mfdir + mf, 0600) - - f = open(instRoot + cf, "w+") - f.write("# silo.conf generated by anaconda\n\n") - - f.write("#boot=%s\n" % (bootDev,)) - f.write("message=%s\n" % (mf,)) - f.write("timeout=%s\n" % (self.timeout or 50)) - - (name, partNum) = getDiskPart(bootDev) - partno = partNum + 1 - f.write("partition=%s\n" % (partno,)) - - if self.password: - f.write("password=%s\n" % (self.password,)) - f.write("restricted\n") - - f.write("default=%s\n" % (kernelList[0][0],)) - f.write("\n") - - rootDev = fsset.getEntryByMountPoint("/").device.getDevice() - - for (label, longlabel, version) in kernelList: - kernelTag = "-" + version - kernelFile = "%s/vmlinuz%s" % (cfPath, kernelTag) - - f.write("image=%s\n" % (kernelFile,)) - f.write("\tlabel=%s\n" % (label,)) - f.write("\tread-only\n") - - initrd = self.makeInitrd(kernelTag) - if os.access(instRoot + initrd, os.R_OK): - f.write("\tinitrd=%s/initrd%s.img\n" % (cfPath, kernelTag)) - - append = "%s" % (self.args.get(),) - - realroot = getRootDevName(initrd, fsset, rootDev, instRoot) - if rootIsDevice(realroot): - f.write("\troot=%s\n" % (realroot,)) - else: - if len(append) > 0: - append = "%s root=%s" % (append, realroot) - else: - append = "root=%s" % (realroot,) - - if len(append) > 0: - f.write("\tappend=\"%s\"\n" % (append,)) - f.write("\n") - - f.close() - os.chmod(instRoot + cf, 0600) - - # FIXME: hack to make sure things are written to disk - import isys - isys.sync() - isys.sync() - isys.sync() - - backup = "%s/backup.b" % (cfPath,) - sbinargs = ["/sbin/silo", "-f", "-C", cf, "-S", backup] - # TODO!!! FIXME!!! XXX!!! - # butil is not defined!!! - assume this is in rhpl now? - if butil.getSparcMachine() == "sun4u": - sbinargs += ["-u"] - else: - sbinargs += ["-U"] - - if not flags.test: - rhpl.executil.execWithRedirect(sbinargs[0], - sbinargs, - stdout = "/dev/tty5", - stderr = "/dev/tty5", - root = instRoot) - - if (not os.access(instRoot + "/etc/silo.conf", os.R_OK) and - os.access(instRoot + "/boot/etc/silo.conf", os.R_OK)): - os.symlink("../boot/etc/silo.conf", - instRoot + "/etc/silo.conf") - - return "" - - def setPassword(self, val, isCrypted = 1): - # silo just handles the password unencrypted - self.password = val - - def write(self, instRoot, fsset, bl, kernelList, chainList, - defaultDev, justConfig, intf): - if len(kernelList) >= 1: - self.writeSilo(instRoot, fsset, bl, kernelList, chainList, - defaultDev, justConfig) - else: - self.noKernelsWarn(intf) - - def __init__(self): - bootloaderInfo.__init__(self) - self.useSiloVal = 1 - self.kernelLocation = "/boot" - self._configdir = "/etc" - self._configname = "silo.conf" - -############### -# end of boot loader objects... these are just some utility functions used - -def rootIsDevice(dev): - if dev.startswith("LABEL=") or dev.startswith("UUID="): - return False - return True - -# hackery to determine if we should do root=LABEL=/ or whatnot -# as usual, knows too much about anaconda -def getRootDevName(initrd, fsset, rootDev, instRoot): - if not os.access(instRoot + initrd, os.R_OK): - return "/dev/%s" % (rootDev,) - - try: - rootEntry = fsset.getEntryByMountPoint("/") - if rootEntry.getUuid() is not None: - return "UUID=%s" %(rootEntry.getUuid(),) - elif rootEntry.getLabel() is not None and rootEntry.device.doLabel is not None: - return "LABEL=%s" %(rootEntry.getLabel(),) - return "/dev/%s" %(rootDev,) - except: - return "/dev/%s" %(rootDev,) diff --git a/booty/ia64.py b/booty/ia64.py new file mode 100644 index 000000000..3f1e88393 --- /dev/null +++ b/booty/ia64.py @@ -0,0 +1,39 @@ +from bootloaderInfo import * + +class ia64BootloaderInfo(efiBootloaderInfo): + def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, + chainList, defaultDev): + config = bootloaderInfo.getBootloaderConfig(self, instRoot, fsset, + bl, kernelList, chainList, + defaultDev) + # altix boxes need relocatable (#120851) + config.addEntry("relocatable") + + return config + + def writeLilo(self, instRoot, fsset, bl, kernelList, + chainList, defaultDev, justConfig): + config = self.getBootloaderConfig(instRoot, fsset, bl, + kernelList, chainList, defaultDev) + config.write(instRoot + self.configfile, perms = 0755) + + return "" + + def write(self, instRoot, fsset, bl, kernelList, chainList, + defaultDev, justConfig, intf): + if len(kernelList) >= 1: + out = self.writeLilo(instRoot, fsset, bl, kernelList, + chainList, defaultDev, justConfig) + else: + self.noKernelsWarn(intf) + + self.removeOldEfiEntries(instRoot) + self.addNewEfiEntry(instRoot, fsset) + + def makeInitrd(self, kernelTag): + return "/boot/efi/EFI/redhat/initrd%s.img" % kernelTag + + def __init__(self): + efiBootloaderInfo.__init__(self) + self._configname = "elilo.conf" + self._bootloader = "elilo.efi" diff --git a/booty/ppc.py b/booty/ppc.py new file mode 100644 index 000000000..26881ec80 --- /dev/null +++ b/booty/ppc.py @@ -0,0 +1,179 @@ +import string +import os + +from bootloaderInfo import * +import fsset +import rhpl + +class ppcBootloaderInfo(bootloaderInfo): + def getBootDevs(self, fs, bl): + import fsset + + devs = [] + machine = rhpl.getPPCMachine() + + if machine == 'pSeries': + for entry in fs.entries: + if isinstance(entry.fsystem, fsset.prepbootFileSystem) \ + and entry.format: + devs.append('/dev/%s' % (entry.device.getDevice(),)) + elif machine == 'PMac': + for entry in fs.entries: + if isinstance(entry.fsystem, fsset.applebootstrapFileSystem) \ + and entry.format: + devs.append('/dev/%s' % (entry.device.getDevice(),)) + + if len(devs) == 0: + # Try to get a boot device; bplan OF understands ext3 + if machine == 'Pegasos' or machine == 'Efika': + entry = fs.getEntryByMountPoint('/boot') + # Try / if we don't have this we're not going to work + if not entry: + entry = fs.getEntryByMountPoint('/') + if entry: + dev = "/dev/%s" % (entry.device.getDevice(asBoot=1),) + devs.append(dev) + else: + if bl.getDevice(): + devs.append("/dev/%s" % bl.getDevice()) + return devs + + + def writeYaboot(self, instRoot, fsset, bl, kernelList, + chainList, defaultDev, justConfigFile): + + yabootTarget = string.join(self.getBootDevs(fsset, bl)) + + bootDev = fsset.getEntryByMountPoint("/boot") + if bootDev: + cf = "/boot/etc/yaboot.conf" + cfPath = "" + if not os.path.isdir(instRoot + "/boot/etc"): + os.mkdir(instRoot + "/boot/etc") + else: + bootDev = fsset.getEntryByMountPoint("/") + cfPath = "/boot" + cf = "/etc/yaboot.conf" + bootDev = bootDev.device.getDevice(asBoot = 1) + + f = open(instRoot + cf, "w+") + + f.write("# yaboot.conf generated by anaconda\n\n") + + f.write("boot=%s\n" %(yabootTarget,)) + f.write("init-message=\"Welcome to %s!\\nHit for boot options\"\n\n" + % productName) + + (name, partNum) = getDiskPart(bootDev) + partno = partNum + 1 # 1 based + + f.write("partition=%s\n" %(partno,)) + + f.write("timeout=%s\n" % (self.timeout or 80)) + f.write("install=/usr/lib/yaboot/yaboot\n") + f.write("delay=5\n") + f.write("enablecdboot\n") + f.write("enableofboot\n") + f.write("enablenetboot\n") + + yabootProg = "/sbin/mkofboot" + if rhpl.getPPCMachine() == "PMac": + # write out the first hfs/hfs+ partition as being macosx + for (label, longlabel, device) in chainList: + if ((not label) or (label == "")): + continue + f.write("macosx=/dev/%s\n" %(device,)) + break + + f.write("magicboot=/usr/lib/yaboot/ofboot\n") + + elif rhpl.getPPCMachine() == "pSeries": + f.write("nonvram\n") + f.write("fstype=raw\n") + + else: # Default non-destructive case for anything else. + f.write("nonvram\n") + f.write("mntpoint=/boot/yaboot\n") + f.write("usemount\n") + if not os.access(instRoot + "/boot/yaboot", os.R_OK): + os.mkdir(instRoot + "/boot/yaboot") + yabootProg = "/sbin/ybin" + + if self.password: + f.write("password=%s\n" %(self.password,)) + f.write("restricted\n") + + f.write("\n") + + rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + + for (label, longlabel, version) in kernelList: + kernelTag = "-" + version + kernelFile = "%s/vmlinuz%s" %(cfPath, kernelTag) + + f.write("image=%s\n" %(kernelFile,)) + f.write("\tlabel=%s\n" %(label,)) + f.write("\tread-only\n") + + initrd = self.makeInitrd(kernelTag) + if os.access(instRoot + initrd, os.R_OK): + f.write("\tinitrd=%s/initrd%s.img\n" %(cfPath,kernelTag)) + + append = "%s" %(self.args.get(),) + + realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + if rootIsDevice(realroot): + f.write("\troot=%s\n" %(realroot,)) + else: + if len(append) > 0: + append = "%s root=%s" %(append,realroot) + else: + append = "root=%s" %(realroot,) + + if len(append) > 0: + f.write("\tappend=\"%s\"\n" %(append,)) + f.write("\n") + + f.close() + os.chmod(instRoot + cf, 0600) + + # FIXME: hack to make sure things are written to disk + import isys + isys.sync() + isys.sync() + isys.sync() + + ybinargs = [ yabootProg, "-f", "-C", cf ] + + if not flags.test: + rhpl.executil.execWithRedirect(ybinargs[0], + ybinargs, + stdout = "/dev/tty5", + stderr = "/dev/tty5", + root = instRoot) + + if (not os.access(instRoot + "/etc/yaboot.conf", os.R_OK) and + os.access(instRoot + "/boot/etc/yaboot.conf", os.R_OK)): + os.symlink("../boot/etc/yaboot.conf", + instRoot + "/etc/yaboot.conf") + + return "" + + def setPassword(self, val, isCrypted = 1): + # yaboot just handles the password and doesn't care if its crypted + # or not + self.password = val + + def write(self, instRoot, fsset, bl, kernelList, chainList, + defaultDev, justConfig, intf): + if len(kernelList) >= 1: + out = self.writeYaboot(instRoot, fsset, bl, kernelList, + chainList, defaultDev, justConfig) + else: + self.noKernelsWarn(intf) + + def __init__(self): + bootloaderInfo.__init__(self) + self.useYabootVal = 1 + self.kernelLocation = "/boot" + self.configfile = "/etc/yaboot.conf" diff --git a/booty/s390.py b/booty/s390.py new file mode 100644 index 000000000..d48030d55 --- /dev/null +++ b/booty/s390.py @@ -0,0 +1,179 @@ +import os + +from bootloaderInfo import * +import fsset +import rhpl + +class s390BootloaderInfo(bootloaderInfo): + def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, + chainList, defaultDev): + images = bl.images.getImages() + + # on upgrade read in the lilo config file + lilo = LiloConfigFile () + self.perms = 0600 + if os.access (instRoot + self.configfile, os.R_OK): + self.perms = os.stat(instRoot + self.configfile)[0] & 0777 + lilo.read (instRoot + self.configfile) + os.rename(instRoot + self.configfile, + instRoot + self.configfile + '.rpmsave') + + # Remove any invalid entries that are in the file; we probably + # just removed those kernels. + for label in lilo.listImages(): + (fsType, sl, path, other) = lilo.getImage(label) + if fsType == "other": continue + + if not os.access(instRoot + sl.getPath(), os.R_OK): + lilo.delImage(label) + + rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + if not rootDev: + raise RuntimeError, "Installing zipl, but there is no root device" + + if rootDev == defaultDev: + lilo.addEntry("default", kernelList[0][0]) + else: + lilo.addEntry("default", chainList[0][0]) + + for (label, longlabel, version) in kernelList: + kernelTag = "-" + version + kernelFile = self.kernelLocation + "vmlinuz" + kernelTag + + try: + lilo.delImage(label) + except IndexError, msg: + pass + + sl = LiloConfigFile(imageType = "image", path = kernelFile) + + initrd = self.makeInitrd(kernelTag) + + sl.addEntry("label", label) + if os.access (instRoot + initrd, os.R_OK): + sl.addEntry("initrd", + "%sinitrd%s.img" %(self.kernelLocation, kernelTag)) + + sl.addEntry("read-only") + sl.addEntry("root", '/dev/' + rootDev) + sl.addEntry("ipldevice", '/dev/' + rootDev[:-1]) + + if self.args.get(): + sl.addEntry('append', '"%s"' % self.args.get()) + + lilo.addImage (sl) + + for (label, longlabel, device) in chainList: + if ((not label) or (label == "")): + continue + try: + (fsType, sl, path, other) = lilo.getImage(label) + lilo.delImage(label) + except IndexError: + sl = LiloConfigFile(imageType = "other", + path = "/dev/%s" %(device)) + sl.addEntry("optional") + + sl.addEntry("label", label) + lilo.addImage (sl) + + # Sanity check #1. There could be aliases in sections which conflict + # with the new images we just created. If so, erase those aliases + imageNames = {} + for label in lilo.listImages(): + imageNames[label] = 1 + + for label in lilo.listImages(): + (fsType, sl, path, other) = lilo.getImage(label) + if sl.testEntry('alias'): + alias = sl.getEntry('alias') + if imageNames.has_key(alias): + sl.delEntry('alias') + imageNames[alias] = 1 + + # Sanity check #2. If single-key is turned on, go through all of + # the image names (including aliases) (we just built the list) and + # see if single-key will still work. + if lilo.testEntry('single-key'): + singleKeys = {} + turnOff = 0 + for label in imageNames.keys(): + l = label[0] + if singleKeys.has_key(l): + turnOff = 1 + singleKeys[l] = 1 + if turnOff: + lilo.delEntry('single-key') + + return lilo + + def writeChandevConf(self, bl, instroot): # S/390 only + cf = "/etc/chandev.conf" + self.perms = 0644 + if bl.args.chandevget(): + fd = os.open(instroot + "/etc/chandev.conf", + os.O_WRONLY | os.O_CREAT) + os.write(fd, "noauto\n") + for cdev in bl.args.chandevget(): + os.write(fd,'%s\n' % cdev) + os.close(fd) + return "" + + + def writeZipl(self, instRoot, fsset, bl, kernelList, chainList, + defaultDev, justConfigFile): + images = bl.images.getImages() + rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + + cf = '/etc/zipl.conf' + self.perms = 0600 + if os.access (instRoot + cf, os.R_OK): + self.perms = os.stat(instRoot + cf)[0] & 0777 + os.rename(instRoot + cf, + instRoot + cf + '.rpmsave') + + f = open(instRoot + cf, "w+") + + f.write('[defaultboot]\n') + f.write('default=' + kernelList[0][0] + '\n') + f.write('target=%s\n' % (self.kernelLocation)) + + cfPath = "/boot/" + for (label, longlabel, version) in kernelList: + kernelTag = "-" + version + kernelFile = "%svmlinuz%s" % (cfPath, kernelTag) + + initrd = self.makeInitrd(kernelTag) + f.write('[%s]\n' % (label)) + f.write('\timage=%s\n' % (kernelFile)) + if os.access (instRoot + initrd, os.R_OK): + f.write('\tramdisk=%sinitrd%s.img\n' %(self.kernelLocation, + kernelTag)) + realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + f.write('\tparameters="root=%s' %(realroot,)) + if bl.args.get(): + f.write(' %s' % (bl.args.get())) + f.write('"\n') + + f.close() + + if not justConfigFile: + argv = [ "/sbin/zipl" ] + rhpl.executil.execWithRedirect(argv[0], argv, root = instRoot, + stdout = "/dev/stdout", + stderr = "/dev/stderr") + + return "" + + def write(self, instRoot, fsset, bl, kernelList, chainList, + defaultDev, justConfig, intf): + out = self.writeZipl(instRoot, fsset, bl, kernelList, + chainList, defaultDev, + justConfig | (not self.useZiplVal)) + out = self.writeChandevConf(bl, instRoot) + + def __init__(self): + bootloaderInfo.__init__(self) + self.useZiplVal = 1 # only used on s390 + self.kernelLocation = "/boot/" + self.configfile = "/etc/zipl.conf" diff --git a/booty/sparc.py b/booty/sparc.py new file mode 100644 index 000000000..471caa060 --- /dev/null +++ b/booty/sparc.py @@ -0,0 +1,127 @@ +import os + +from bootloaderInfo import * +import fsset +import rhpl + +class sparcBootloaderInfo(bootloaderInfo): + def writeSilo(self, instRoot, fsset, bl, kernelList, + chainList, defaultDev, justConfigFile): + + bootDev = fsset.getEntryByMountPoint("/boot") + mf = '/silo.message' + if bootDev: + cf = "/boot/silo.conf" + mfdir = '/boot' + cfPath = "" + if not os.path.isdir(instRoot + "/boot"): + os.mkdir(instRoot + "/boot") + else: + bootDev = fsset.getEntryByMountPoint("/") + cf = "/etc/silo.conf" + mfdir = '/etc' + cfPath = "/boot" + bootDev = bootDev.device.getDevice(asBoot = 1) + + f = open(instRoot + mfdir + mf, "w+") + f.write("Welcome to %s!\nHit for boot options\n\n" % productName) + f.close() + os.chmod(instRoot + mfdir + mf, 0600) + + f = open(instRoot + cf, "w+") + f.write("# silo.conf generated by anaconda\n\n") + + f.write("#boot=%s\n" % (bootDev,)) + f.write("message=%s\n" % (mf,)) + f.write("timeout=%s\n" % (self.timeout or 50)) + + (name, partNum) = getDiskPart(bootDev) + partno = partNum + 1 + f.write("partition=%s\n" % (partno,)) + + if self.password: + f.write("password=%s\n" % (self.password,)) + f.write("restricted\n") + + f.write("default=%s\n" % (kernelList[0][0],)) + f.write("\n") + + rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + + for (label, longlabel, version) in kernelList: + kernelTag = "-" + version + kernelFile = "%s/vmlinuz%s" % (cfPath, kernelTag) + + f.write("image=%s\n" % (kernelFile,)) + f.write("\tlabel=%s\n" % (label,)) + f.write("\tread-only\n") + + initrd = self.makeInitrd(kernelTag) + if os.access(instRoot + initrd, os.R_OK): + f.write("\tinitrd=%s/initrd%s.img\n" % (cfPath, kernelTag)) + + append = "%s" % (self.args.get(),) + + realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + if rootIsDevice(realroot): + f.write("\troot=%s\n" % (realroot,)) + else: + if len(append) > 0: + append = "%s root=%s" % (append, realroot) + else: + append = "root=%s" % (realroot,) + + if len(append) > 0: + f.write("\tappend=\"%s\"\n" % (append,)) + f.write("\n") + + f.close() + os.chmod(instRoot + cf, 0600) + + # FIXME: hack to make sure things are written to disk + import isys + isys.sync() + isys.sync() + isys.sync() + + backup = "%s/backup.b" % (cfPath,) + sbinargs = ["/sbin/silo", "-f", "-C", cf, "-S", backup] + # TODO!!! FIXME!!! XXX!!! + # butil is not defined!!! - assume this is in rhpl now? + if butil.getSparcMachine() == "sun4u": + sbinargs += ["-u"] + else: + sbinargs += ["-U"] + + if not flags.test: + rhpl.executil.execWithRedirect(sbinargs[0], + sbinargs, + stdout = "/dev/tty5", + stderr = "/dev/tty5", + root = instRoot) + + if (not os.access(instRoot + "/etc/silo.conf", os.R_OK) and + os.access(instRoot + "/boot/etc/silo.conf", os.R_OK)): + os.symlink("../boot/etc/silo.conf", + instRoot + "/etc/silo.conf") + + return "" + + def setPassword(self, val, isCrypted = 1): + # silo just handles the password unencrypted + self.password = val + + def write(self, instRoot, fsset, bl, kernelList, chainList, + defaultDev, justConfig, intf): + if len(kernelList) >= 1: + self.writeSilo(instRoot, fsset, bl, kernelList, chainList, + defaultDev, justConfig) + else: + self.noKernelsWarn(intf) + + def __init__(self): + bootloaderInfo.__init__(self) + self.useSiloVal = 1 + self.kernelLocation = "/boot" + self._configdir = "/etc" + self._configname = "silo.conf" diff --git a/booty/x86.py b/booty/x86.py new file mode 100644 index 000000000..535aaab84 --- /dev/null +++ b/booty/x86.py @@ -0,0 +1,567 @@ +import os +import string + +from bootloaderInfo import * +import checkbootloader +import fsset +import iutil +import rhpl + +class x86BootloaderInfo(efiBootloaderInfo): + def setPassword(self, val, isCrypted = 1): + if not val: + self.password = val + self.pure = val + return + + if isCrypted and self.useGrubVal == 0: + self.pure = None + return + elif isCrypted: + self.password = val + self.pure = None + else: + salt = "$1$" + saltLen = 8 + + saltchars = string.letters + string.digits + './' + for i in range(saltLen): + salt += random.choice(saltchars) + + self.password = crypt.crypt(val, salt) + self.pure = val + + def getPassword (self): + return self.pure + + def setForceLBA(self, val): + self.forceLBA32 = val + + def setUseGrub(self, val): + self.useGrubVal = val + + def getPhysicalDevices(self, device): + # This finds a list of devices on which the given device name resides. + # Accepted values for "device" are raid1 md devices (i.e. "md0"), + # physical disks ("hda"), and real partitions on physical disks + # ("hda1"). Volume groups/logical volumes are not accepted. + # + # XXX this has internal anaconda-ish knowledge. ick. + import isys + import lvm + + if string.split(device, '/', 1)[0] in map (lambda vg: vg[0], + lvm.vglist()): + return [] + + if device.startswith("mapper/luks-"): + return [] + + if device.startswith('md'): + bootable = 0 + parts = checkbootloader.getRaidDisks(device, 1, stripPart=0) + parts.sort() + return parts + + return [device] + + def runGrubInstall(self, instRoot, bootDev, cmds, cfPath): + if cfPath == "/": + syncDataToDisk(bootDev, "/boot", instRoot) + else: + syncDataToDisk(bootDev, "/", instRoot) + + # copy the stage files over into /boot + rhpl.executil.execWithRedirect( "/sbin/grub-install", + ["/sbin/grub-install", "--just-copy"], + stdout = "/dev/tty5", stderr = "/dev/tty5", + root = instRoot) + + # really install the bootloader + for cmd in cmds: + p = os.pipe() + os.write(p[1], cmd + '\n') + os.close(p[1]) + + # FIXME: hack to try to make sure everything is written + # to the disk + if cfPath == "/": + syncDataToDisk(bootDev, "/boot", instRoot) + else: + syncDataToDisk(bootDev, "/", instRoot) + + rhpl.executil.execWithRedirect('/sbin/grub' , + [ "grub", "--batch", "--no-floppy", + "--device-map=/boot/grub/device.map" ], + stdin = p[0], + stdout = "/dev/tty5", stderr = "/dev/tty5", + root = instRoot) + os.close(p[0]) + + def installGrub(self, instRoot, bootDevs, grubTarget, grubPath, fsset, + target, cfPath): + if iutil.isEfi(): + efiBootloaderInfo.installGrub(self, instRoot, bootDevs, grubTarget, + grubPath, fsset, target, cfPath) + return + + args = "--stage2=/boot/grub/stage2 " + if self.forceLBA32: + args = "%s--force-lba " % (args,) + + cmds = [] + for bootDev in bootDevs: + gtPart = self.getMatchingPart(bootDev, grubTarget) + gtDisk = self.grubbyPartitionName(fsset.getDiskPart(gtPart)[0]) + bPart = self.grubbyPartitionName(bootDev) + cmd = "root %s\n" % (bPart,) + + stage1Target = gtDisk + if target == "partition": + stage1Target = self.grubbyPartitionName(gtPart) + + cmd += "install %s%s/stage1 d %s %s/stage2 p %s%s/grub.conf" % \ + (args, grubPath, stage1Target, grubPath, bPart, grubPath) + cmds.append(cmd) + + self.runGrubInstall(instRoot, bootDev, cmds, cfPath) + + def writeGrub(self, instRoot, fsset, bl, kernelList, chainList, + defaultDev, justConfigFile): + + images = bl.images.getImages() + rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + + # XXX old config file should be read here for upgrade + + cf = "%s%s" % (instRoot, self.configfile) + self.perms = 0600 + if os.access (cf, os.R_OK): + self.perms = os.stat(cf)[0] & 0777 + os.rename(cf, cf + '.rpmsave') + + grubTarget = bl.getDevice() + target = "mbr" + if (grubTarget.startswith('rd/') or grubTarget.startswith('ida/') or + grubTarget.startswith('cciss/') or + grubTarget.startswith('sx8/') or + grubTarget.startswith('mapper/')): + if grubTarget[-1].isdigit(): + if grubTarget[-2] == 'p' or \ + (grubTarget[-2].isdigit() and grubTarget[-3] == 'p'): + target = "partition" + elif grubTarget[-1].isdigit() and not grubTarget.startswith('md'): + target = "partition" + + f = open(cf, "w+") + + f.write("# grub.conf generated by anaconda\n") + f.write("#\n") + f.write("# Note that you do not have to rerun grub " + "after making changes to this file\n") + + bootDev = fsset.getEntryByMountPoint("/boot") + grubPath = "/grub" + cfPath = "/" + if not bootDev: + bootDev = fsset.getEntryByMountPoint("/") + grubPath = "/boot/grub" + cfPath = "/boot/" + f.write("# NOTICE: You do not have a /boot partition. " + "This means that\n") + f.write("# all kernel and initrd paths are relative " + "to /, eg.\n") + else: + f.write("# NOTICE: You have a /boot partition. This means " + "that\n") + f.write("# all kernel and initrd paths are relative " + "to /boot/, eg.\n") + + bootDevs = self.getPhysicalDevices(bootDev.device.getDevice()) + bootDev = bootDev.device.getDevice() + + f.write('# root %s\n' % self.grubbyPartitionName(bootDevs[0])) + f.write("# kernel %svmlinuz-version ro " + "root=/dev/%s\n" % (cfPath, rootDev)) + f.write("# initrd %sinitrd-version.img\n" % (cfPath)) + f.write("#boot=/dev/%s\n" % (grubTarget)) + + # get the default image to boot... we have to walk and find it + # since grub indexes by where it is in the config file + if defaultDev == rootDev: + default = 0 + else: + # if the default isn't linux, it's the first thing in the + # chain list + default = len(kernelList) + + # keep track of which devices are used for the device.map + usedDevs = {} + + f.write('default=%s\n' % (default)) + f.write('timeout=%d\n' % (self.timeout or 0)) + + if self.serial == 1: + # grub the 0-based number of the serial console device + unit = self.serialDevice[-1] + + # and we want to set the speed too + speedend = 0 + for char in self.serialOptions: + if char not in string.digits: + break + speedend = speedend + 1 + if speedend != 0: + speed = self.serialOptions[:speedend] + else: + # reasonable default + speed = "9600" + + f.write("serial --unit=%s --speed=%s\n" %(unit, speed)) + f.write("terminal --timeout=%s serial console\n" % (self.timeout or 5)) + else: + # we only want splashimage if they're not using a serial console + if os.access("%s/boot/grub/splash.xpm.gz" %(instRoot,), os.R_OK): + f.write('splashimage=%s%sgrub/splash.xpm.gz\n' + % (self.grubbyPartitionName(bootDevs[0]), cfPath)) + f.write("hiddenmenu\n") + + for dev in self.getPhysicalDevices(grubTarget): + usedDevs[dev] = 1 + + if self.password: + f.write('password --md5 %s\n' %(self.password)) + + for (label, longlabel, version) in kernelList: + kernelTag = "-" + version + kernelFile = "%svmlinuz%s" % (cfPath, kernelTag) + + initrd = self.makeInitrd(kernelTag) + + f.write('title %s (%s)\n' % (longlabel, version)) + f.write('\troot %s\n' % self.grubbyPartitionName(bootDevs[0])) + + realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + realroot = " root=%s" %(realroot,) + + if version.endswith("xen0") or (version.endswith("xen") and not os.path.exists("/proc/xen")): + # hypervisor case + sermap = { "ttyS0": "com1", "ttyS1": "com2", + "ttyS2": "com3", "ttyS3": "com4" } + if self.serial and sermap.has_key(self.serialDevice) and \ + self.serialOptions: + hvs = "%s=%s" %(sermap[self.serialDevice], + self.serialOptions) + else: + hvs = "" + if version.endswith("xen0"): + hvFile = "%sxen.gz-%s %s" %(cfPath, + version.replace("xen0", ""), + hvs) + else: + hvFile = "%sxen.gz-%s %s" %(cfPath, + version.replace("xen", ""), + hvs) + f.write('\tkernel %s\n' %(hvFile,)) + f.write('\tmodule %s ro%s' %(kernelFile, realroot)) + if self.args.get(): + f.write(' %s' % self.args.get()) + f.write('\n') + + if os.access (instRoot + initrd, os.R_OK): + f.write('\tmodule %sinitrd%s.img\n' % (cfPath, kernelTag)) + else: # normal kernel + f.write('\tkernel %s ro%s' % (kernelFile, realroot)) + if self.args.get(): + f.write(' %s' % self.args.get()) + f.write('\n') + + if os.access (instRoot + initrd, os.R_OK): + f.write('\tinitrd %sinitrd%s.img\n' % (cfPath, kernelTag)) + + for (label, longlabel, device) in chainList: + if ((not longlabel) or (longlabel == "")): + continue + f.write('title %s\n' % (longlabel)) + f.write('\trootnoverify %s\n' % self.grubbyPartitionName(device)) +# f.write('\tmakeactive\n') + f.write('\tchainloader +1') + f.write('\n') + usedDevs[device] = 1 + + f.close() + + if not "/efi/" in cf: + os.chmod(cf, self.perms) + + try: + # make symlink for menu.lst (default config file name) + menulst = "%s%s/menu.lst" % (instRoot, self.configdir) + if os.access (menulst, os.R_OK): + os.rename(menulst, menulst + ".rpmsave") + os.symlink("./grub.conf", menulst) + except: + pass + + try: + # make symlink for /etc/grub.conf (config files belong in /etc) + etcgrub = "%s%s" % (instRoot, "/etc/grub.conf") + if os.access (etcgrub, os.R_OK): + os.rename(etcgrub, etcgrub + ".rpmsave") + os.symlink(".." + self.configfile, etcgrub) + except: + pass + + for dev in self.getPhysicalDevices(rootDev) + bootDevs: + usedDevs[dev] = 1 + + if os.access(instRoot + "/boot/grub/device.map", os.R_OK): + os.rename(instRoot + "/boot/grub/device.map", + instRoot + "/boot/grub/device.map.rpmsave") + if 1: # not os.access(instRoot + "/boot/grub/device.map", os.R_OK): + f = open(instRoot + "/boot/grub/device.map", "w+") + f.write("# this device map was generated by anaconda\n") + devs = usedDevs.keys() + usedDevs = {} + for dev in devs: + drive = fsset.getDiskPart(dev)[0] + if usedDevs.has_key(drive): + continue + usedDevs[drive] = 1 + devs = usedDevs.keys() + devs.sort() + for drive in devs: + # XXX hack city. If they're not the sort of thing that'll + # be in the device map, they shouldn't still be in the list. + if not drive.startswith('md'): + f.write("(%s) /dev/%s\n" % (self.grubbyDiskName(drive), + drive)) + f.close() + + sysconf = '/etc/sysconfig/grub' + if os.access (instRoot + sysconf, os.R_OK): + self.perms = os.stat(instRoot + sysconf)[0] & 0777 + os.rename(instRoot + sysconf, + instRoot + sysconf + '.rpmsave') + # if it's an absolute symlink, just get it out of our way + elif (os.path.islink(instRoot + sysconf) and + os.readlink(instRoot + sysconf)[0] == '/'): + os.rename(instRoot + sysconf, + instRoot + sysconf + '.rpmsave') + f = open(instRoot + sysconf, 'w+') + f.write("boot=/dev/%s\n" %(grubTarget,)) + # XXX forcelba never gets read back... + if self.forceLBA32: + f.write("forcelba=1\n") + else: + f.write("forcelba=0\n") + f.close() + + if not justConfigFile: + self.installGrub(instRoot, bootDevs, grubTarget, grubPath, fsset, \ + target, cfPath) + + return "" + + def getMatchingPart(self, bootDev, target): + bootName, bootPartNum = fsset.getDiskPart(bootDev) + devices = self.getPhysicalDevices(target) + for device in devices: + name, partNum = fsset.getDiskPart(device) + if name == bootName: + return device + return devices[0] + + def grubbyDiskName(self, name): + return "hd%d" % self.drivelist.index(name) + + def grubbyPartitionName(self, dev): + (name, partNum) = fsset.getDiskPart(dev) + if partNum != None: + return "(%s,%d)" % (self.grubbyDiskName(name), partNum) + else: + return "(%s)" %(self.grubbyDiskName(name)) + + + def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, + chainList, defaultDev): + config = bootloaderInfo.getBootloaderConfig(self, instRoot, fsset, + bl, kernelList, chainList, + defaultDev) + + liloTarget = bl.getDevice() + + config.addEntry("boot", '/dev/' + liloTarget, replace = 0) + config.addEntry("map", "/boot/map", replace = 0) + config.addEntry("install", "/boot/boot.b", replace = 0) + message = "/boot/message" + + if self.pure is not None and not self.useGrubVal: + config.addEntry("restricted", replace = 0) + config.addEntry("password", self.pure, replace = 0) + + if self.serial == 1: + # grab the 0-based number of the serial console device + unit = self.serialDevice[-1] + # FIXME: we should probably put some options, but lilo + # only supports up to 9600 baud so just use the defaults + # it's better than nothing :( + config.addEntry("serial=%s" %(unit,)) + else: + # message screws up serial console + if os.access(instRoot + message, os.R_OK): + config.addEntry("message", message, replace = 0) + + if not config.testEntry('lba32'): + if self.forceLBA32 or (bl.above1024 and + rhpl.getArch() != "x86_64"): + config.addEntry("lba32", replace = 0) + + return config + + # this is a hackish function that depends on the way anaconda writes + # out the grub.conf with a #boot= comment + # XXX this falls into the category of self.doUpgradeOnly + def upgradeGrub(self, instRoot, fsset, bl, kernelList, chainList, + defaultDev, justConfigFile): + if justConfigFile: + return "" + + theDev = None + for (fn, stanza) in [ ("/etc/sysconfig/grub", "boot="), + ("/boot/grub/grub.conf", "#boot=") ]: + try: + f = open(instRoot + fn, "r") + except: + continue + + # the following bits of code are straight from checkbootloader.py + lines = f.readlines() + f.close() + for line in lines: + if line.startswith(stanza): + theDev = checkbootloader.getBootDevString(line) + break + if theDev is not None: + break + + if theDev is None: + # we could find the dev before, but can't now... cry about it + return "" + + # migrate info to /etc/sysconfig/grub + self.writeSysconfig(instRoot, theDev) + + # more suckage. grub-install can't work without a valid /etc/mtab + # so we have to do shenanigans to get updated grub installed... + # steal some more code above + bootDev = fsset.getEntryByMountPoint("/boot") + grubPath = "/grub" + cfPath = "/" + if not bootDev: + bootDev = fsset.getEntryByMountPoint("/") + grubPath = "/boot/grub" + cfPath = "/boot/" + + masterBootDev = bootDev.device.getDevice(asBoot = 0) + if masterBootDev[0:2] == 'md': + rootDevs = checkbootloader.getRaidDisks(masterBootDev, raidLevel=1, + stripPart = 0) + else: + rootDevs = [masterBootDev] + + if theDev[5:7] == 'md': + stage1Devs = checkbootloader.getRaidDisks(theDev[5:], raidLevel=1) + else: + stage1Devs = [theDev[5:]] + + for stage1Dev in stage1Devs: + # cross fingers; if we can't find a root device on the same + # hardware as this boot device, we just blindly hope the first + # thing in the list works. + + grubbyStage1Dev = self.grubbyPartitionName(stage1Dev) + + grubbyRootPart = self.grubbyPartitionName(rootDevs[0]) + + for rootDev in rootDevs: + testGrubbyRootDev = fsset.getDiskPart(rootDev)[0] + testGrubbyRootDev = self.grubbyPartitionName(testGrubbyRootDev) + + if grubbyStage1Dev == testGrubbyRootDev: + grubbyRootPart = self.grubbyPartitionName(rootDev) + break + + args = "--stage2=/boot/grub/stage2 " + cmd ="root %s" % (grubbyRootPart,) + cmds = [ cmd ] + cmd = "install %s%s/stage1 d %s %s/stage2 p %s%s/grub.conf" \ + % (args, grubPath, grubbyStage1Dev, grubPath, grubbyRootPart, + grubPath) + cmds.append(cmd) + + if not justConfigFile: + self.runGrubInstall(instRoot, bootDev.device.setupDevice(), + cmds, cfPath) + + return "" + + def writeSysconfig(self, instRoot, installDev): + sysconf = '/etc/sysconfig/grub' + if not os.access(instRoot + sysconf, os.R_OK): + f = open(instRoot + sysconf, "w+") + f.write("boot=%s\n" %(installDev,)) + # XXX forcelba never gets read back at all... + if self.forceLBA32: + f.write("forcelba=1\n") + else: + f.write("forcelba=0\n") + f.close() + + def write(self, instRoot, fsset, bl, kernelList, chainList, + defaultDev, justConfig, intf): + if self.timeout is None and chainList: + self.timeout = 5 + + # XXX HACK ALERT - see declaration above + if self.doUpgradeOnly: + if self.useGrubVal: + self.upgradeGrub(instRoot, fsset, bl, kernelList, + chainList, defaultDev, justConfig) + return + + if len(kernelList) < 1: + self.noKernelsWarn(intf) + + out = self.writeGrub(instRoot, fsset, bl, kernelList, + chainList, defaultDev, + justConfig | (not self.useGrubVal)) + + # XXX move the lilo.conf out of the way if they're using GRUB + # so that /sbin/installkernel does a more correct thing + if self.useGrubVal and os.access(instRoot + '/etc/lilo.conf', os.R_OK): + os.rename(instRoot + "/etc/lilo.conf", + instRoot + "/etc/lilo.conf.anaconda") + + + def getArgList(self): + args = bootloaderInfo.getArgList(self) + + if self.forceLBA32: + args.append("--lba32") + if self.password: + args.append("--md5pass=%s" %(self.password)) + + return args + + def __init__(self): + bootloaderInfo.__init__(self) + efiBootloaderInfo.__init__(self, initialize=False) + + self._configdir = "/boot/grub" + self._configname = "grub.conf" + # XXX use checkbootloader to determine what to default to + self.useGrubVal = 1 + self.kernelLocation = "/boot/" + self.password = None + self.pure = None -- cgit From 930cac9c0de1480543f25c411110d9f615e34693 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 23 Feb 2009 14:17:43 -0500 Subject: Update booty to work with the new pyparted (hdegoede). --- booty/bootloaderInfo.py | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index b33c11885..73d2781d3 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -234,13 +234,6 @@ class BootImages: foundDos = 0 for (dev, type) in diskSet.partitionTypes(): if type in dosFilesystems and not foundDos and doesDualBoot(): - import isys - import partedUtils - - part = partedUtils.get_partition_by_name(diskSet.disks, dev) - if part.native_type not in partedUtils.dosPartitionTypes: - continue - try: bootable = checkForBootBlock('/dev/' + dev) devs.append((dev, type)) @@ -254,12 +247,14 @@ class BootImages: # be the correct one to boot with XP using ntfs foundDos = 1 elif type in ('hfs', 'hfs+') and rhpl.getPPCMachine() == "PMac": - import isys - import partedUtils - - part = partedUtils.get_partition_by_name(diskSet.disks, dev) - if partedUtils.get_flags(part) != "boot": - devs.append((dev, type)) + import _ped + + for disk in diskset.disks: + part = disk.getPartitionByPath('/dev/' + dev) + if part: + if not part.getFlag(_ped.PARTITION_BOOT): + devs.append((dev, type)) + break slash = fsset.getEntryByMountPoint('/') if not slash or not slash.device or not slash.fsystem: -- cgit From f00ec6b554eece6689e71c50ff0eb59ad2549b54 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 26 Feb 2009 11:33:40 -0500 Subject: Don't use rhpl.executil anymore. --- booty/alpha.py | 6 +++--- booty/bootloaderInfo.py | 27 +++++++++++++-------------- booty/ppc.py | 11 ++++++----- booty/s390.py | 4 ++-- booty/sparc.py | 11 +++++------ booty/x86.py | 20 ++++++++++---------- 6 files changed, 39 insertions(+), 40 deletions(-) diff --git a/booty/alpha.py b/booty/alpha.py index 5e62c93ca..d56c4cadd 100644 --- a/booty/alpha.py +++ b/booty/alpha.py @@ -1,5 +1,5 @@ import os -import rhpl.executil +import iutil from bootloaderInfo import * import fsset @@ -109,7 +109,7 @@ class alphaBootloaderInfo(bootloaderInfo): # to and the second argument is a path to the bootstrap loader # file. args = ("swriteboot", ("/dev/%s" % wbd), "/boot/bootlx") - rhpl.executil.execWithRedirect ('/sbin/swriteboot', args, + iutil.execWithRedirect ('/sbin/swriteboot', args, root = instRoot, stdout = "/dev/tty5", stderr = "/dev/tty5") @@ -120,7 +120,7 @@ class alphaBootloaderInfo(bootloaderInfo): # It's always the boot partition whether it's / or /boot (with # the mount point being omitted.) args = ("abootconf", ("/dev/%s" % wbd), str (bdpn)) - rhpl.executil.execWithRedirect ('/sbin/abootconf', args, + iutil.execWithRedirect ('/sbin/abootconf', args, root = instRoot, stdout = "/dev/tty5", stderr = "/dev/tty5") diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 73d2781d3..b44219310 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -27,7 +27,6 @@ from copy import copy from lilo import LiloConfigFile import rhpl from rhpl.translate import _, N_ -import rhpl.executil from flags import flags from fsset import getDiskPart @@ -67,16 +66,16 @@ def syncDataToDisk(dev, mntpt, instRoot = "/"): # and xfs is even more "special" (#117968) if isys.readFSType(dev) == "xfs": - rhpl.executil.execWithRedirect( "/usr/sbin/xfs_freeze", - ["/usr/sbin/xfs_freeze", "-f", mntpt], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - root = instRoot) - rhpl.executil.execWithRedirect( "/usr/sbin/xfs_freeze", - ["/usr/sbin/xfs_freeze", "-u", mntpt], - stdout = "/dev/tty5", - stderr = "/dev/tty5", - root = instRoot) + iutil.execWithRedirect("/usr/sbin/xfs_freeze", + ["/usr/sbin/xfs_freeze", "-f", mntpt], + stdout = "/dev/tty5", + stderr = "/dev/tty5", + root = instRoot) + iutil.execWithRedirect("/usr/sbin/xfs_freeze", + ["/usr/sbin/xfs_freeze", "-u", mntpt], + stdout = "/dev/tty5", + stderr = "/dev/tty5", + root = instRoot) def rootIsDevice(dev): if dev.startswith("LABEL=") or dev.startswith("UUID="): @@ -589,7 +588,7 @@ class efiBootloaderInfo(bootloaderInfo): # XXX wouldn't it be nice to have a real interface to use efibootmgr from? def removeOldEfiEntries(self, instRoot): p = os.pipe() - rhpl.executil.execWithRedirect('/usr/sbin/efibootmgr', ["efibootmgr"], + iutil.execWithRedirect('/usr/sbin/efibootmgr', ["efibootmgr"], root = instRoot, stdout = p[1]) os.close(p[1]) @@ -606,7 +605,7 @@ class efiBootloaderInfo(bootloaderInfo): continue if string.join(fields[1:], " ") == productName: entry = fields[0][4:8] - rhpl.executil.execWithRedirect('/usr/sbin/efibootmgr', + iutil.execWithRedirect('/usr/sbin/efibootmgr', ["efibootmgr", "-b", entry, "-B"], root = instRoot, stdout="/dev/tty5", stderr="/dev/tty5") @@ -636,7 +635,7 @@ class efiBootloaderInfo(bootloaderInfo): argv = [ "/usr/sbin/efibootmgr", "-c" , "-w", "-L", productName, "-d", "/dev/%s" % bootdisk, "-p", bootpart, "-l", "\\EFI\\redhat\\" + self.bootloader ] - rhpl.executil.execWithRedirect(argv[0], argv, root = instRoot, + iutil.execWithRedirect(argv[0], argv, root = instRoot, stdout = "/dev/tty5", stderr = "/dev/tty5") diff --git a/booty/ppc.py b/booty/ppc.py index 26881ec80..24a5df8fa 100644 --- a/booty/ppc.py +++ b/booty/ppc.py @@ -3,6 +3,7 @@ import os from bootloaderInfo import * import fsset +import iutil import rhpl class ppcBootloaderInfo(bootloaderInfo): @@ -146,11 +147,11 @@ class ppcBootloaderInfo(bootloaderInfo): ybinargs = [ yabootProg, "-f", "-C", cf ] if not flags.test: - rhpl.executil.execWithRedirect(ybinargs[0], - ybinargs, - stdout = "/dev/tty5", - stderr = "/dev/tty5", - root = instRoot) + iutil.execWithRedirect(ybinargs[0], + ybinargs, + stdout = "/dev/tty5", + stderr = "/dev/tty5", + root = instRoot) if (not os.access(instRoot + "/etc/yaboot.conf", os.R_OK) and os.access(instRoot + "/boot/etc/yaboot.conf", os.R_OK)): diff --git a/booty/s390.py b/booty/s390.py index d48030d55..a1857363c 100644 --- a/booty/s390.py +++ b/booty/s390.py @@ -2,7 +2,7 @@ import os from bootloaderInfo import * import fsset -import rhpl +import iutil class s390BootloaderInfo(bootloaderInfo): def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, @@ -159,7 +159,7 @@ class s390BootloaderInfo(bootloaderInfo): if not justConfigFile: argv = [ "/sbin/zipl" ] - rhpl.executil.execWithRedirect(argv[0], argv, root = instRoot, + iutil.execWithRedirect(argv[0], argv, root = instRoot, stdout = "/dev/stdout", stderr = "/dev/stderr") diff --git a/booty/sparc.py b/booty/sparc.py index 471caa060..a9cfaf83f 100644 --- a/booty/sparc.py +++ b/booty/sparc.py @@ -2,7 +2,6 @@ import os from bootloaderInfo import * import fsset -import rhpl class sparcBootloaderInfo(bootloaderInfo): def writeSilo(self, instRoot, fsset, bl, kernelList, @@ -94,11 +93,11 @@ class sparcBootloaderInfo(bootloaderInfo): sbinargs += ["-U"] if not flags.test: - rhpl.executil.execWithRedirect(sbinargs[0], - sbinargs, - stdout = "/dev/tty5", - stderr = "/dev/tty5", - root = instRoot) + iutil.execWithRedirect(sbinargs[0], + sbinargs, + stdout = "/dev/tty5", + stderr = "/dev/tty5", + root = instRoot) if (not os.access(instRoot + "/etc/silo.conf", os.R_OK) and os.access(instRoot + "/boot/etc/silo.conf", os.R_OK)): diff --git a/booty/x86.py b/booty/x86.py index 535aaab84..0ce86053c 100644 --- a/booty/x86.py +++ b/booty/x86.py @@ -72,10 +72,10 @@ class x86BootloaderInfo(efiBootloaderInfo): syncDataToDisk(bootDev, "/", instRoot) # copy the stage files over into /boot - rhpl.executil.execWithRedirect( "/sbin/grub-install", - ["/sbin/grub-install", "--just-copy"], - stdout = "/dev/tty5", stderr = "/dev/tty5", - root = instRoot) + iutil.execWithRedirect("/sbin/grub-install", + ["/sbin/grub-install", "--just-copy"], + stdout = "/dev/tty5", stderr = "/dev/tty5", + root = instRoot) # really install the bootloader for cmd in cmds: @@ -90,12 +90,12 @@ class x86BootloaderInfo(efiBootloaderInfo): else: syncDataToDisk(bootDev, "/", instRoot) - rhpl.executil.execWithRedirect('/sbin/grub' , - [ "grub", "--batch", "--no-floppy", - "--device-map=/boot/grub/device.map" ], - stdin = p[0], - stdout = "/dev/tty5", stderr = "/dev/tty5", - root = instRoot) + iutil.execWithRedirect('/sbin/grub' , + [ "grub", "--batch", "--no-floppy", + "--device-map=/boot/grub/device.map" ], + stdin = p[0], + stdout = "/dev/tty5", stderr = "/dev/tty5", + root = instRoot) os.close(p[0]) def installGrub(self, instRoot, bootDevs, grubTarget, grubPath, fsset, -- cgit From 61b67230cbd20837cc4ac763bcaf56e1199f5cfe Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 27 Feb 2009 10:10:12 -0500 Subject: Update packaging and makefile system for the new booty module. --- Makefile | 4 ++-- anaconda | 6 ------ anaconda.spec | 3 +-- booty/Makefile | 33 +++++++++++++++++++++++++++++++++ runpychecker.sh | 4 ++-- scripts/upd-instroot | 3 +-- 6 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 booty/Makefile diff --git a/Makefile b/Makefile index deacdef23..eed63f625 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,7 @@ VERSION := $(shell awk '/Version:/ { print $$2 }' anaconda.spec) RELEASE := $(shell awk '/Release:/ { print $$2 }' anaconda.spec) CVSROOT ?= ${CVSROOT:-$(shell cat CVS/Root 2>/dev/null)} -SUBDIRS = isys loader po \ +SUBDIRS = isys loader po booty \ textw utils scripts bootdisk installclasses \ iw pixmaps command-stubs ui docs # fonts aren't on s390/s390x @@ -40,7 +40,7 @@ ifneq (,$(filter i386 x86_64,$(ARCH))) SUBDIRS += gptsync endif -PYCHECKERPATH=isys:textw:iw:installclasses:/usr/lib/booty:/usr/share/system-config-date +PYCHECKERPATH=isys:textw:iw:installclasses:/usr/share/system-config-date PYCHECKEROPTS=-F pycheckrc-for-anaconda CATALOGS = po/anaconda.pot diff --git a/anaconda b/anaconda index 6ded5dbb8..4e328b8c3 100755 --- a/anaconda +++ b/anaconda @@ -352,12 +352,6 @@ def setupPythonPath(): sys.path.insert(1, '/usr/lib/anaconda/textw') sys.path.insert(2, '/usr/lib/anaconda/iw') - if (os.path.exists('booty')): - sys.path.append('booty') - sys.path.append('booty/edd') - else: - sys.path.append('/usr/lib/booty') - sys.path.append('/usr/share/system-config-date') def addPoPath(dir): diff --git a/anaconda.spec b/anaconda.spec index c581e907f..21a2071bf 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -43,7 +43,6 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) %define pythoncryptsetupver 0.0.6 BuildRequires: audit-libs-devel -BuildRequires: booty BuildRequires: bzip2-devel BuildRequires: device-mapper-devel >= %{dmver} BuildRequires: e2fsprogs-devel >= %{e2fsver} @@ -84,7 +83,6 @@ Requires: policycoreutils Requires: rpm-python >= %{rpmpythonver} Requires: comps-extras Requires: rhpl >= %{rhplver} -Requires: booty Requires: parted >= %{partedver} Requires: pyparted >= %{pypartedver} Requires: yum >= %{yumver} @@ -146,6 +144,7 @@ Obsoletes: anaconda-images <= 10 Provides: anaconda-images = %{version}-%{release} Obsoletes: anaconda-runtime < %{version}-%{release} Provides: anaconda-runtime = %{version}-%{release} +Obsoletes: booty %description The anaconda package contains the program which was used to install your diff --git a/booty/Makefile b/booty/Makefile new file mode 100644 index 000000000..1730985e2 --- /dev/null +++ b/booty/Makefile @@ -0,0 +1,33 @@ +# +# Makefile +# +# Copyright (C) 2009 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +include ../Makefile.inc + +all: + echo "nothing to make" + +install: + mkdir -p $(DESTDIR)/$(PYTHONLIBDIR)/booty + install -p -m 644 *.py $(DESTDIR)/$(PYTHONLIBDIR)/booty + ../py-compile --basedir $(DESTDIR)/$(PYTHONLIBDIR)/booty $(DESTDIR)/$(PYTHONLIBDIR)/booty/*.py + +clean: + rm -f *.o *.so *.pyc + +depend: diff --git a/runpychecker.sh b/runpychecker.sh index 2a330ef85..033017493 100755 --- a/runpychecker.sh +++ b/runpychecker.sh @@ -37,7 +37,7 @@ if [ "`tail -c 1 pychecker-false-positives`" == "`echo`" ]; then exit 1 fi -export PYTHONPATH="isys:textw:iw:installclasses:/usr/lib/booty:/usr/share/system-config-date" +export PYTHONPATH="isys:textw:iw:installclasses:/usr/share/system-config-date" pychecker --only --limit 1000 \ --maxlines 500 --maxargs 20 --maxbranches 80 --maxlocals 60 --maxreturns 20 \ @@ -45,7 +45,7 @@ pychecker --only --limit 1000 \ --no-import --no-miximport --no-pkgimport --no-reimport \ --no-argsused --no-varargsused --no-override \ $NON_STRICT_OPTIONS \ - anaconda anaconda *.py textw/*.py iw/*.py installclasses/*.py isys/*.py | \ + anaconda anaconda *.py textw/*.py iw/*.py installclasses/*.py isys/*.py booty/*.py booty/*/*.py | \ egrep -v "`cat $FALSE_POSITIVES | tr '\n' '|'`" > pychecker-log if [ -s pychecker-log ]; then diff --git a/scripts/upd-instroot b/scripts/upd-instroot index a26a5231e..b7dfca23f 100755 --- a/scripts/upd-instroot +++ b/scripts/upd-instroot @@ -152,7 +152,7 @@ die () { PACKAGES="GConf2 NetworkManager ORBit2 PolicyKit acl anaconda anaconda-yum-plugins at-spi atk attr audit-libs bash bitmap-fonts-cjk - booty btrfs-progs busybox-anaconda bzip2 bzip2-libs cairo cjkunifonts-uming + btrfs-progs busybox-anaconda bzip2 bzip2-libs cairo cjkunifonts-uming comps-extras coreutils cpio cracklib cracklib-dicts cracklib-python cryptsetup-luks db4 dbus dbus-python dejavu-sans-fonts dejavu-sans-mono-fonts device-mapper @@ -496,7 +496,6 @@ usr/lib/anaconda-runtime usr/lib/anaconda/installclasses usr/lib/anaconda/iw usr/lib/anaconda/textw -usr/lib/booty usr/lib/kernel-wrapper usr/lib/locale usr/lib/python?.?/site-packages/bugzilla* -- cgit From 8bed1806f592a600366353908e24e3c0435c268b Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 27 Feb 2009 16:23:10 -0500 Subject: Fix some booty import errors. --- bootloader.py | 2 +- iw/timezone_gui.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bootloader.py b/bootloader.py index 22fb0e68d..f12165e9e 100644 --- a/bootloader.py +++ b/bootloader.py @@ -36,7 +36,7 @@ import logging log = logging.getLogger("anaconda") import booty -import bootloaderInfo +from booty import bootloaderInfo def bootloaderSetupChoices(anaconda): if anaconda.dir == DISPATCH_BACK: diff --git a/iw/timezone_gui.py b/iw/timezone_gui.py index 2b6290901..27657a946 100644 --- a/iw/timezone_gui.py +++ b/iw/timezone_gui.py @@ -29,7 +29,7 @@ import sys from timezone_map_gui import TimezoneMap, Enum from iw_gui import * -from bootloaderInfo import dosFilesystems +from booty.bootloaderInfo import dosFilesystems from bootloader import hasWindows from constants import * -- cgit From 1e2313dec51238148b10e2e817b11cc7cbd86b81 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 27 Feb 2009 13:39:00 -0500 Subject: Remove all uses of fsset.getEntryByMountPoint. --- booty/alpha.py | 24 ++++++++--------- booty/bootloaderInfo.py | 70 ++++++++++++++++++++++++------------------------- booty/ia64.py | 14 +++++----- booty/ppc.py | 43 ++++++++++++++++-------------- booty/s390.py | 22 ++++++++-------- booty/sparc.py | 26 +++++++++--------- booty/x86.py | 68 +++++++++++++++++++++++------------------------ 7 files changed, 133 insertions(+), 134 deletions(-) diff --git a/booty/alpha.py b/booty/alpha.py index d56c4cadd..8dfb005ca 100644 --- a/booty/alpha.py +++ b/booty/alpha.py @@ -15,14 +15,14 @@ class alphaBootloaderInfo(bootloaderInfo): (foo, partitionNumber) = getDiskPart(path) return partitionNumber + 1 - def writeAboot(self, instRoot, fsset, bl, kernelList, + def writeAboot(self, instRoot, bl, kernelList, chainList, defaultDev, justConfig): - # Get bootDevice and rootDevice - rootDevice = fsset.getEntryByMountPoint("/").device.getDevice() - if fsset.getEntryByMountPoint("/boot"): - bootDevice = fsset.getEntryByMountPoint("/boot").device.getDevice() - else: + rootDevice = storage.fsset.mountpoints["/"] + try: + bootDevice = storage.fsset.mountpoints["/boot"] + except KeyError: bootDevice = rootDevice + bootnotroot = bootDevice != rootDevice # If /etc/aboot.conf already exists we rename it @@ -66,7 +66,7 @@ class alphaBootloaderInfo(bootloaderInfo): f.write ("# all kernel paths are relative to /boot/\n") # bpn is the boot partition number. - bpn = self.partitionNum(bootDevice) + bpn = self.partitionNum(bootDevice.path) lines = 0 # We write entries line using the following format: @@ -84,7 +84,7 @@ class alphaBootloaderInfo(bootloaderInfo): if os.path.isfile(instRoot + initrd): f.write(" initrd=%sinitrd%s.img" %(kernelPath, kernelTag)) - realroot = getRootDevName(initrd, fsset, rootDevice, instRoot) + realroot = getRootDevName(instRoot+initrd, rootDevice.path) f.write(" root=%s" %(realroot,)) args = self.args.get() @@ -102,8 +102,8 @@ class alphaBootloaderInfo(bootloaderInfo): # Now we're ready to write the relevant boot information. wbd # is the whole boot device, bdpn is the boot device partition # number. - wbd = self.wholeDevice (bootDevice) - bdpn = self.partitionNum (bootDevice) + wbd = self.wholeDevice (bootDevice.path) + bdpn = self.partitionNum (bootDevice.path) # Calling swriteboot. The first argument is the disk to write # to and the second argument is a path to the bootstrap loader @@ -126,12 +126,12 @@ class alphaBootloaderInfo(bootloaderInfo): stderr = "/dev/tty5") - def write(self, instRoot, fsset, bl, kernelList, chainList, + def write(self, instRoot, bl, kernelList, chainList, defaultDev, justConfig, intf): if len(kernelList) < 1: self.noKernelsWarn(intf) - self.writeAboot(instRoot, fsset, bl, kernelList, + self.writeAboot(instRoot, bl, kernelList, chainList, defaultDev, justConfig) def __init__(self): diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index b44219310..ee9a31ca8 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -84,19 +84,19 @@ def rootIsDevice(dev): # hackery to determine if we should do root=LABEL=/ or whatnot # as usual, knows too much about anaconda -def getRootDevName(initrd, fsset, rootDev, instRoot): - if not os.access(instRoot + initrd, os.R_OK): - return "/dev/%s" % (rootDev,) +def getRootDevName(initrd, rootDevice): + if not os.access(initrd, os.R_OK): + return rootDevice.path try: - rootEntry = fsset.getEntryByMountPoint("/") - if rootEntry.getUuid() is not None: - return "UUID=%s" %(rootEntry.getUuid(),) - elif rootEntry.getLabel() is not None and rootEntry.device.doLabel is not None: - return "LABEL=%s" %(rootEntry.getLabel(),) - return "/dev/%s" %(rootDev,) + if rootDevice.uuid: + return "UUID=%s" % rootDevice.uuid + elif rootDevice.label: + return "LABEL=%s" % rootDevice.label + + return rootDevice.path except: - return "/dev/%s" %(rootDev,) + return rootDevice.path class BootyNoKernelWarning: def __init__ (self, value=""): @@ -221,8 +221,8 @@ class BootImages: if not self.images.has_key(self.default): - entry = fsset.getEntryByMountPoint('/') - self.default = entry.device.getDevice() + device = storage.fsset.mountpoints["/"] + self.default = device (label, longlabel, type) = self.images[self.default] if not label: self.images[self.default] = ("linux", productName, type) @@ -255,18 +255,19 @@ class BootImages: devs.append((dev, type)) break - slash = fsset.getEntryByMountPoint('/') - if not slash or not slash.device or not slash.fsystem: + try: + rootDevice = storage.fsset.mountpoints["/"] + except KeyError: + rootDevice = None + + if not rootDevice or not rootDevice.format.bootable: raise ValueError, ("Trying to pick boot devices but do not have a " "sane root partition. Aborting install.") - devs.append((slash.device.getDevice(), slash.fsystem.getName())) + devs.append((rootDevice, rootDevice.format.type)) devs.sort() - return devs - - class bootloaderInfo: def getConfigFileName(self): if not self._configname: @@ -317,12 +318,7 @@ class bootloaderInfo: def makeInitrd(self, kernelTag): return "/boot/initrd%s.img" % kernelTag - # XXX need to abstract out the requirement for a fsset to be able - # to get it "on the fly" on a running system as well as being able - # to get it how we do now from anaconda. probably by having the - # first thing in the chain create a "fsset" object that has the - # dictionary of mounted filesystems since that's what we care about - def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, + def getBootloaderConfig(self, instRoot, bl, kernelList, chainList, defaultDev): images = bl.images.getImages() @@ -352,8 +348,9 @@ class bootloaderInfo: lilo.addEntry("prompt", replace = 0) lilo.addEntry("timeout", self.timeout or "20", replace = 0) - rootDev = fsset.getEntryByMountPoint("/").device.getDevice() - if not rootDev: + try: + rootDev = storage.fsset.mountpoints["/"] + except KeyError: raise RuntimeError, "Installing lilo, but there is no root device" if rootDev == defaultDev: @@ -382,9 +379,9 @@ class bootloaderInfo: sl.addEntry("read-only") append = "%s" %(self.args.get(),) - realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + realroot = getRootDevName(instRoot+initrd, rootDev.path) if rootIsDevice(realroot): - sl.addEntry("root", '/dev/' + rootDev) + sl.addEntry("root", rootDev.path) else: if len(append) > 0: append = "%s root=%s" %(append,realroot) @@ -440,10 +437,10 @@ class bootloaderInfo: return lilo - def write(self, instRoot, fsset, bl, kernelList, chainList, + def write(self, instRoot, bl, kernelList, chainList, defaultDev, justConfig, intf = None): if len(kernelList) >= 1: - config = self.getBootloaderConfig(instRoot, fsset, bl, + config = self.getBootloaderConfig(instRoot, bl, kernelList, chainList, defaultDev) config.write(instRoot + self.configfile, perms = self.perms) @@ -610,10 +607,11 @@ class efiBootloaderInfo(bootloaderInfo): root = instRoot, stdout="/dev/tty5", stderr="/dev/tty5") - def addNewEfiEntry(self, instRoot, fsset): - bootdev = fsset.getEntryByMountPoint("/boot/efi").device.getDevice() - if not bootdev: - bootdev = fsset.getEntryByDeviceName("sda1").device.getDevice() + def addNewEfiEntry(self, instRoot): + try: + bootdev = storage.fsset.mountpoints["/boot/efi"] + except KeyError: + bootdev = storage.devicetree.getDeviceByName("sda1") link = "%s%s/%s" % (instRoot, "/etc/", self.configname) if not os.access(link, os.R_OK): @@ -639,12 +637,12 @@ class efiBootloaderInfo(bootloaderInfo): stdout = "/dev/tty5", stderr = "/dev/tty5") - def installGrub(self, instRoot, bootDevs, grubTarget, grubPath, fsset, + def installGrub(self, instRoot, bootDevs, grubTarget, grubPath, target, cfPath): if not iutil.isEfi(): raise EnvironmentError self.removeOldEfiEntries(instRoot) - self.addNewEfiEntry(instRoot, fsset) + self.addNewEfiEntry(instRoot) def __init__(self, initialize = True): if initialize: diff --git a/booty/ia64.py b/booty/ia64.py index 3f1e88393..174c696d5 100644 --- a/booty/ia64.py +++ b/booty/ia64.py @@ -1,9 +1,9 @@ from bootloaderInfo import * class ia64BootloaderInfo(efiBootloaderInfo): - def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, + def getBootloaderConfig(self, instRoot, bl, kernelList, chainList, defaultDev): - config = bootloaderInfo.getBootloaderConfig(self, instRoot, fsset, + config = bootloaderInfo.getBootloaderConfig(self, instRoot, bl, kernelList, chainList, defaultDev) # altix boxes need relocatable (#120851) @@ -11,24 +11,24 @@ class ia64BootloaderInfo(efiBootloaderInfo): return config - def writeLilo(self, instRoot, fsset, bl, kernelList, + def writeLilo(self, instRoot, bl, kernelList, chainList, defaultDev, justConfig): - config = self.getBootloaderConfig(instRoot, fsset, bl, + config = self.getBootloaderConfig(instRoot, bl, kernelList, chainList, defaultDev) config.write(instRoot + self.configfile, perms = 0755) return "" - def write(self, instRoot, fsset, bl, kernelList, chainList, + def write(self, instRoot, bl, kernelList, chainList, defaultDev, justConfig, intf): if len(kernelList) >= 1: - out = self.writeLilo(instRoot, fsset, bl, kernelList, + out = self.writeLilo(instRoot, bl, kernelList, chainList, defaultDev, justConfig) else: self.noKernelsWarn(intf) self.removeOldEfiEntries(instRoot) - self.addNewEfiEntry(instRoot, fsset) + self.addNewEfiEntry(instRoot) def makeInitrd(self, kernelTag): return "/boot/efi/EFI/redhat/initrd%s.img" % kernelTag diff --git a/booty/ppc.py b/booty/ppc.py index 24a5df8fa..bf92e71e3 100644 --- a/booty/ppc.py +++ b/booty/ppc.py @@ -27,40 +27,43 @@ class ppcBootloaderInfo(bootloaderInfo): if len(devs) == 0: # Try to get a boot device; bplan OF understands ext3 if machine == 'Pegasos' or machine == 'Efika': - entry = fs.getEntryByMountPoint('/boot') - # Try / if we don't have this we're not going to work - if not entry: - entry = fs.getEntryByMountPoint('/') - if entry: - dev = "/dev/%s" % (entry.device.getDevice(asBoot=1),) - devs.append(dev) + try: + device = storage.fsset.mountpoints["/boot"] + except KeyError: + try: + # Try / if we don't have this we're not going to work + device = storage.fsset.mountpoints["/"] + except KeyError: + return devs + + devs.append(device.path) else: if bl.getDevice(): devs.append("/dev/%s" % bl.getDevice()) return devs - def writeYaboot(self, instRoot, fsset, bl, kernelList, + def writeYaboot(self, instRoot, bl, kernelList, chainList, defaultDev, justConfigFile): - yabootTarget = string.join(self.getBootDevs(fsset, bl)) + yabootTarget = string.join(self.getBootDevs(bl)) + + try: + bootDev = storage.fsset.mountpoints["/boot"] - bootDev = fsset.getEntryByMountPoint("/boot") - if bootDev: cf = "/boot/etc/yaboot.conf" cfPath = "" if not os.path.isdir(instRoot + "/boot/etc"): os.mkdir(instRoot + "/boot/etc") - else: - bootDev = fsset.getEntryByMountPoint("/") + except KeyError: + bootDev = storage.fsset.mountpoints["/"] + cfPath = "/boot" cf = "/etc/yaboot.conf" - bootDev = bootDev.device.getDevice(asBoot = 1) f = open(instRoot + cf, "w+") f.write("# yaboot.conf generated by anaconda\n\n") - f.write("boot=%s\n" %(yabootTarget,)) f.write("init-message=\"Welcome to %s!\\nHit for boot options\"\n\n" % productName) @@ -105,8 +108,8 @@ class ppcBootloaderInfo(bootloaderInfo): f.write("restricted\n") f.write("\n") - - rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + + rootDev = storage.fsset.mountpoints["/"] for (label, longlabel, version) in kernelList: kernelTag = "-" + version @@ -122,7 +125,7 @@ class ppcBootloaderInfo(bootloaderInfo): append = "%s" %(self.args.get(),) - realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + realroot = getRootDevName(instRoot+initrd, rootDev.path) if rootIsDevice(realroot): f.write("\troot=%s\n" %(realroot,)) else: @@ -165,10 +168,10 @@ class ppcBootloaderInfo(bootloaderInfo): # or not self.password = val - def write(self, instRoot, fsset, bl, kernelList, chainList, + def write(self, instRoot, bl, kernelList, chainList, defaultDev, justConfig, intf): if len(kernelList) >= 1: - out = self.writeYaboot(instRoot, fsset, bl, kernelList, + out = self.writeYaboot(instRoot, bl, kernelList, chainList, defaultDev, justConfig) else: self.noKernelsWarn(intf) diff --git a/booty/s390.py b/booty/s390.py index a1857363c..92f640d9d 100644 --- a/booty/s390.py +++ b/booty/s390.py @@ -1,11 +1,10 @@ import os from bootloaderInfo import * -import fsset import iutil class s390BootloaderInfo(bootloaderInfo): - def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, + def getBootloaderConfig(self, instRoot, bl, kernelList, chainList, defaultDev): images = bl.images.getImages() @@ -27,8 +26,9 @@ class s390BootloaderInfo(bootloaderInfo): if not os.access(instRoot + sl.getPath(), os.R_OK): lilo.delImage(label) - rootDev = fsset.getEntryByMountPoint("/").device.getDevice() - if not rootDev: + try: + rootDev = storage.fsset.mountpoints["/"] + except KeyError: raise RuntimeError, "Installing zipl, but there is no root device" if rootDev == defaultDev: @@ -55,8 +55,8 @@ class s390BootloaderInfo(bootloaderInfo): "%sinitrd%s.img" %(self.kernelLocation, kernelTag)) sl.addEntry("read-only") - sl.addEntry("root", '/dev/' + rootDev) - sl.addEntry("ipldevice", '/dev/' + rootDev[:-1]) + sl.addEntry("root", rootDev.path) + sl.addEntry("ipldevice", rootDev.path[:-1]) if self.args.get(): sl.addEntry('append', '"%s"' % self.args.get()) @@ -120,10 +120,10 @@ class s390BootloaderInfo(bootloaderInfo): return "" - def writeZipl(self, instRoot, fsset, bl, kernelList, chainList, + def writeZipl(self, instRoot, bl, kernelList, chainList, defaultDev, justConfigFile): images = bl.images.getImages() - rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + rootDev = storage.fsset.mountpoints["/"] cf = '/etc/zipl.conf' self.perms = 0600 @@ -149,7 +149,7 @@ class s390BootloaderInfo(bootloaderInfo): if os.access (instRoot + initrd, os.R_OK): f.write('\tramdisk=%sinitrd%s.img\n' %(self.kernelLocation, kernelTag)) - realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + realroot = getRootDevName(instRoot+initrd, rootDev.path) f.write('\tparameters="root=%s' %(realroot,)) if bl.args.get(): f.write(' %s' % (bl.args.get())) @@ -165,9 +165,9 @@ class s390BootloaderInfo(bootloaderInfo): return "" - def write(self, instRoot, fsset, bl, kernelList, chainList, + def write(self, instRoot, bl, kernelList, chainList, defaultDev, justConfig, intf): - out = self.writeZipl(instRoot, fsset, bl, kernelList, + out = self.writeZipl(instRoot, bl, kernelList, chainList, defaultDev, justConfig | (not self.useZiplVal)) out = self.writeChandevConf(bl, instRoot) diff --git a/booty/sparc.py b/booty/sparc.py index a9cfaf83f..1de44927d 100644 --- a/booty/sparc.py +++ b/booty/sparc.py @@ -1,26 +1,26 @@ import os from bootloaderInfo import * -import fsset class sparcBootloaderInfo(bootloaderInfo): - def writeSilo(self, instRoot, fsset, bl, kernelList, + def writeSilo(self, instRoot, bl, kernelList, chainList, defaultDev, justConfigFile): - bootDev = fsset.getEntryByMountPoint("/boot") - mf = '/silo.message' - if bootDev: + try: + bootDev = storage.fsset.mountpoints["/boot"] + + mf = '/silo.message' cf = "/boot/silo.conf" mfdir = '/boot' cfPath = "" if not os.path.isdir(instRoot + "/boot"): os.mkdir(instRoot + "/boot") - else: - bootDev = fsset.getEntryByMountPoint("/") + except KeyError: + bootDev = storage.fsset.mountpoints["/"] + cf = "/etc/silo.conf" mfdir = '/etc' cfPath = "/boot" - bootDev = bootDev.device.getDevice(asBoot = 1) f = open(instRoot + mfdir + mf, "w+") f.write("Welcome to %s!\nHit for boot options\n\n" % productName) @@ -30,7 +30,7 @@ class sparcBootloaderInfo(bootloaderInfo): f = open(instRoot + cf, "w+") f.write("# silo.conf generated by anaconda\n\n") - f.write("#boot=%s\n" % (bootDev,)) + f.write("#boot=%s\n" % (bootDev.path,)) f.write("message=%s\n" % (mf,)) f.write("timeout=%s\n" % (self.timeout or 50)) @@ -45,7 +45,7 @@ class sparcBootloaderInfo(bootloaderInfo): f.write("default=%s\n" % (kernelList[0][0],)) f.write("\n") - rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + rootDev = storage.fsset.mountpoints["/"] for (label, longlabel, version) in kernelList: kernelTag = "-" + version @@ -61,7 +61,7 @@ class sparcBootloaderInfo(bootloaderInfo): append = "%s" % (self.args.get(),) - realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + realroot = getRootDevName(instRoot+initrd, rootDev.path) if rootIsDevice(realroot): f.write("\troot=%s\n" % (realroot,)) else: @@ -110,10 +110,10 @@ class sparcBootloaderInfo(bootloaderInfo): # silo just handles the password unencrypted self.password = val - def write(self, instRoot, fsset, bl, kernelList, chainList, + def write(self, instRoot, bl, kernelList, chainList, defaultDev, justConfig, intf): if len(kernelList) >= 1: - self.writeSilo(instRoot, fsset, bl, kernelList, chainList, + self.writeSilo(instRoot, bl, kernelList, chainList, defaultDev, justConfig) else: self.noKernelsWarn(intf) diff --git a/booty/x86.py b/booty/x86.py index 0ce86053c..3046cf7e5 100644 --- a/booty/x86.py +++ b/booty/x86.py @@ -98,11 +98,11 @@ class x86BootloaderInfo(efiBootloaderInfo): root = instRoot) os.close(p[0]) - def installGrub(self, instRoot, bootDevs, grubTarget, grubPath, fsset, + def installGrub(self, instRoot, bootDevs, grubTarget, grubPath, target, cfPath): if iutil.isEfi(): efiBootloaderInfo.installGrub(self, instRoot, bootDevs, grubTarget, - grubPath, fsset, target, cfPath) + grubPath, target, cfPath) return args = "--stage2=/boot/grub/stage2 " @@ -126,11 +126,11 @@ class x86BootloaderInfo(efiBootloaderInfo): self.runGrubInstall(instRoot, bootDev, cmds, cfPath) - def writeGrub(self, instRoot, fsset, bl, kernelList, chainList, + def writeGrub(self, instRoot, bl, kernelList, chainList, defaultDev, justConfigFile): images = bl.images.getImages() - rootDev = fsset.getEntryByMountPoint("/").device.getDevice() + rootDev = storage.fsset.mountpoints["/"] # XXX old config file should be read here for upgrade @@ -160,29 +160,27 @@ class x86BootloaderInfo(efiBootloaderInfo): f.write("# Note that you do not have to rerun grub " "after making changes to this file\n") - bootDev = fsset.getEntryByMountPoint("/boot") - grubPath = "/grub" - cfPath = "/" - if not bootDev: - bootDev = fsset.getEntryByMountPoint("/") + try: + bootDev = storage.fsset.mountpoints["/boot"] + grubPath = "/grub" + cfPath = "/" + f.write("# NOTICE: You have a /boot partition. This means " + "that\n") + f.write("# all kernel and initrd paths are relative " + "to /boot/, eg.\n") + except KeyError: + bootDev = storage.fsset.mountpoints["/"] grubPath = "/boot/grub" cfPath = "/boot/" f.write("# NOTICE: You do not have a /boot partition. " "This means that\n") f.write("# all kernel and initrd paths are relative " "to /, eg.\n") - else: - f.write("# NOTICE: You have a /boot partition. This means " - "that\n") - f.write("# all kernel and initrd paths are relative " - "to /boot/, eg.\n") - bootDevs = self.getPhysicalDevices(bootDev.device.getDevice()) - bootDev = bootDev.device.getDevice() + bootDevs = self.getPhysicalDevices(bootDev) f.write('# root %s\n' % self.grubbyPartitionName(bootDevs[0])) - f.write("# kernel %svmlinuz-version ro " - "root=/dev/%s\n" % (cfPath, rootDev)) + f.write("# kernel %svmlinuz-version ro root=%s\n" % (cfPath, rootDev.path)) f.write("# initrd %sinitrd-version.img\n" % (cfPath)) f.write("#boot=/dev/%s\n" % (grubTarget)) @@ -241,7 +239,7 @@ class x86BootloaderInfo(efiBootloaderInfo): f.write('title %s (%s)\n' % (longlabel, version)) f.write('\troot %s\n' % self.grubbyPartitionName(bootDevs[0])) - realroot = getRootDevName(initrd, fsset, rootDev, instRoot) + realroot = getRootDevName(instRoot+initrd, rootDev.path) realroot = " root=%s" %(realroot,) if version.endswith("xen0") or (version.endswith("xen") and not os.path.exists("/proc/xen")): @@ -358,7 +356,7 @@ class x86BootloaderInfo(efiBootloaderInfo): f.close() if not justConfigFile: - self.installGrub(instRoot, bootDevs, grubTarget, grubPath, fsset, \ + self.installGrub(instRoot, bootDevs, grubTarget, grubPath, \ target, cfPath) return "" @@ -383,9 +381,9 @@ class x86BootloaderInfo(efiBootloaderInfo): return "(%s)" %(self.grubbyDiskName(name)) - def getBootloaderConfig(self, instRoot, fsset, bl, kernelList, + def getBootloaderConfig(self, instRoot, bl, kernelList, chainList, defaultDev): - config = bootloaderInfo.getBootloaderConfig(self, instRoot, fsset, + config = bootloaderInfo.getBootloaderConfig(self, instRoot, bl, kernelList, chainList, defaultDev) @@ -422,7 +420,7 @@ class x86BootloaderInfo(efiBootloaderInfo): # this is a hackish function that depends on the way anaconda writes # out the grub.conf with a #boot= comment # XXX this falls into the category of self.doUpgradeOnly - def upgradeGrub(self, instRoot, fsset, bl, kernelList, chainList, + def upgradeGrub(self, instRoot, bl, kernelList, chainList, defaultDev, justConfigFile): if justConfigFile: return "" @@ -455,21 +453,22 @@ class x86BootloaderInfo(efiBootloaderInfo): # more suckage. grub-install can't work without a valid /etc/mtab # so we have to do shenanigans to get updated grub installed... # steal some more code above - bootDev = fsset.getEntryByMountPoint("/boot") - grubPath = "/grub" - cfPath = "/" - if not bootDev: - bootDev = fsset.getEntryByMountPoint("/") + try: + bootDev = storage.fsset.mountpoints["/boot"] + grubPath = "/grub" + cfPath = "/" + except KeyError: + bootDev = storage.fsset.mountpoints["/"] grubPath = "/boot/grub" cfPath = "/boot/" - masterBootDev = bootDev.device.getDevice(asBoot = 0) + masterBootDev = bootDev if masterBootDev[0:2] == 'md': rootDevs = checkbootloader.getRaidDisks(masterBootDev, raidLevel=1, stripPart = 0) else: rootDevs = [masterBootDev] - + if theDev[5:7] == 'md': stage1Devs = checkbootloader.getRaidDisks(theDev[5:], raidLevel=1) else: @@ -501,8 +500,7 @@ class x86BootloaderInfo(efiBootloaderInfo): cmds.append(cmd) if not justConfigFile: - self.runGrubInstall(instRoot, bootDev.device.setupDevice(), - cmds, cfPath) + self.runGrubInstall(instRoot, bootDev cmds, cfPath) return "" @@ -518,7 +516,7 @@ class x86BootloaderInfo(efiBootloaderInfo): f.write("forcelba=0\n") f.close() - def write(self, instRoot, fsset, bl, kernelList, chainList, + def write(self, instRoot, bl, kernelList, chainList, defaultDev, justConfig, intf): if self.timeout is None and chainList: self.timeout = 5 @@ -526,14 +524,14 @@ class x86BootloaderInfo(efiBootloaderInfo): # XXX HACK ALERT - see declaration above if self.doUpgradeOnly: if self.useGrubVal: - self.upgradeGrub(instRoot, fsset, bl, kernelList, + self.upgradeGrub(instRoot, bl, kernelList, chainList, defaultDev, justConfig) return if len(kernelList) < 1: self.noKernelsWarn(intf) - out = self.writeGrub(instRoot, fsset, bl, kernelList, + out = self.writeGrub(instRoot, bl, kernelList, chainList, defaultDev, justConfig | (not self.useGrubVal)) -- cgit From ca9d5901228243c02e5db5ae9fe6faa0f9f37bc9 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 27 Feb 2009 14:26:17 -0500 Subject: Make some sense out of the types in the BootImages class. --- booty/bootloaderInfo.py | 90 +++++++++++++++++++++++-------------------------- 1 file changed, 43 insertions(+), 47 deletions(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index ee9a31ca8..befefa0d0 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -183,14 +183,6 @@ class BootImages: # return a copy so users can modify it w/o affecting us return copy(self.images) - - def setImageLabel(self, dev, label, setLong = 0): - orig = self.images[dev] - if setLong: - self.images[dev] = (orig[0], label, orig[2]) - else: - self.images[dev] = (label, orig[1], orig[2]) - def setDefault(self, default): # default is a device self.default = default @@ -198,62 +190,66 @@ class BootImages: def getDefault(self): return self.default - # XXX this has internal anaconda-ish knowledge. ick - def setup(self, diskSet, fsset): + # Construct a dictionary mapping device names to (OS, product, type) + # tuples. + def setup(self, storage): devices = {} - devs = self.availableBootDevices(diskSet, fsset) - for (dev, type) in devs: - devices[dev] = 1 + bootDevs = self.availableBootDevices(storage) + + for (dev, type) in bootDevs: + devices[dev.name] = 1 # These partitions have disappeared for dev in self.images.keys(): - if not devices.has_key(dev): del self.images[dev] + if not devices.has_key(dev): + del self.images[dev] # These have appeared - for (dev, type) in devs: - if not self.images.has_key(dev): + for (dev, type) in bootDevs: + if not self.images.has_key(dev.name): if type in dosFilesystems and doesDualBoot(): - self.images[dev] = ("Other", "Other", type) + self.images[dev.name] = ("Other", "Other", type) elif type in ("hfs", "hfs+") and rhpl.getPPCMachine() == "PMac": - self.images[dev] = ("Other", "Other", type) + self.images[dev.name] = ("Other", "Other", type) else: - self.images[dev] = (None, None, type) - + self.images[dev.name] = (None, None, type) if not self.images.has_key(self.default): - device = storage.fsset.mountpoints["/"] - self.default = device + self.default = storage.fsset.rootDevice.name (label, longlabel, type) = self.images[self.default] if not label: self.images[self.default] = ("linux", productName, type) - # XXX more internal anaconda knowledge - def availableBootDevices(self, diskSet, fsset): - devs = [] - foundDos = 0 - for (dev, type) in diskSet.partitionTypes(): + # Return a list of (storage.Device, string) tuples that are bootable + # devices. The string is the type of the device, which is just a string + # like "vfat" or "swap" or "lvm". + def availableBootDevices(self, storage): + import parted + retval = [] + foundDos = False + + for part in [p for p in storage.partitions if p.exists]: + # Skip extended, metadata, freespace, etc. + if part.partType not in (parted.PARTITION_NORMAL, parted.PARTITION_LOGICAL) or not part.format: + continue + + type = part.format.type + if type in dosFilesystems and not foundDos and doesDualBoot(): try: - bootable = checkForBootBlock('/dev/' + dev) - devs.append((dev, type)) - foundDos = 1 - except Exception, e: + bootable = checkForBootBlock(part.path) + retval.append((part, type)) + foundDos = True + except: pass - elif ((type == 'ntfs' or type =='hpfs') and not foundDos - and doesDualBoot()): - devs.append((dev, type)) + elif type in ["ntfs", "hpfs"] and not foundDos and doesDualBoot(): + retval.append((part, type)) # maybe questionable, but the first ntfs or fat is likely to # be the correct one to boot with XP using ntfs - foundDos = 1 - elif type in ('hfs', 'hfs+') and rhpl.getPPCMachine() == "PMac": - import _ped - - for disk in diskset.disks: - part = disk.getPartitionByPath('/dev/' + dev) - if part: - if not part.getFlag(_ped.PARTITION_BOOT): - devs.append((dev, type)) - break + foundDos = True + elif type in ["hfs", "hfs+"] and rhpl.getPPCMachine() == "PMac": + if self.partedFlags[parted.PARTITION_BOOT]: + retval.append((part, type)) try: rootDevice = storage.fsset.mountpoints["/"] @@ -264,9 +260,9 @@ class BootImages: raise ValueError, ("Trying to pick boot devices but do not have a " "sane root partition. Aborting install.") - devs.append((rootDevice, rootDevice.format.type)) - devs.sort() - return devs + retval.append((rootDevice, rootDevice.format.type)) + retval.sort() + return retval class bootloaderInfo: def getConfigFileName(self): -- cgit From 1e73575ca9dbc1a3a02d977956a20413dd8ec0bb Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 27 Feb 2009 14:29:59 -0500 Subject: There's no need to paste together the full path over and over again. --- booty/alpha.py | 13 +++++++------ booty/bootloaderInfo.py | 17 ++++++++--------- booty/s390.py | 11 ++++++----- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/booty/alpha.py b/booty/alpha.py index 8dfb005ca..977e17ea6 100644 --- a/booty/alpha.py +++ b/booty/alpha.py @@ -25,12 +25,13 @@ class alphaBootloaderInfo(bootloaderInfo): bootnotroot = bootDevice != rootDevice + confFile = instRoot + self.configfile + # If /etc/aboot.conf already exists we rename it # /etc/aboot.conf.rpmsave. - if os.path.isfile(instRoot + self.configfile): - os.rename (instRoot + self.configfile, - instRoot + self.configfile + ".rpmsave") - + if os.path.isfile(confFile): + os.rename (confFile, confFile + ".rpmsave") + # Then we create the necessary files. If the root device isn't # the boot device, we create /boot/etc/ where the aboot.conf # will live, and we create /etc/aboot.conf as a symlink to it. @@ -41,7 +42,7 @@ class alphaBootloaderInfo(bootloaderInfo): # We install the symlink (/etc/aboot.conf has already been # renamed in necessary.) - os.symlink("../boot" + self.configfile, instRoot + self.configfile) + os.symlink("../boot" + self.configfile, confFile) cfPath = instRoot + "/boot" + self.configfile # Kernel path is set to / because a boot partition will @@ -49,7 +50,7 @@ class alphaBootloaderInfo(bootloaderInfo): kernelPath = '/' # Otherwise, we just need to create /etc/aboot.conf. else: - cfPath = instRoot + self.configfile + cfPath = confFile kernelPath = self.kernelLocation # If we already have an aboot.conf, rename it diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index befefa0d0..c149a5461 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -318,19 +318,18 @@ class bootloaderInfo: chainList, defaultDev): images = bl.images.getImages() + confFile = instRoot + self.configfile + # on upgrade read in the lilo config file lilo = LiloConfigFile () self.perms = 0600 - if os.access (instRoot + self.configfile, os.R_OK): - self.perms = os.stat(instRoot + self.configfile)[0] & 0777 - lilo.read (instRoot + self.configfile) - os.rename(instRoot + self.configfile, - instRoot + self.configfile + '.rpmsave') + if os.access (confFile, os.R_OK): + self.perms = os.stat(confFile)[0] & 0777 + lilo.read(confFile) + os.rename(confFile, confFile + ".rpmsave") # if it's an absolute symlink, just get it out of our way - elif (os.path.islink(instRoot + self.configfile) and - os.readlink(instRoot + self.configfile)[0] == '/'): - os.rename(instRoot + self.configfile, - instRoot + self.configfile + '.rpmsave') + elif (os.path.islink(confFile) and os.readlink(confFile)[0] == '/'): + os.rename(confFile, confFile + ".rpmsave") # Remove any invalid entries that are in the file; we probably # just removed those kernels. diff --git a/booty/s390.py b/booty/s390.py index 92f640d9d..a96ac0033 100644 --- a/booty/s390.py +++ b/booty/s390.py @@ -11,11 +11,12 @@ class s390BootloaderInfo(bootloaderInfo): # on upgrade read in the lilo config file lilo = LiloConfigFile () self.perms = 0600 - if os.access (instRoot + self.configfile, os.R_OK): - self.perms = os.stat(instRoot + self.configfile)[0] & 0777 - lilo.read (instRoot + self.configfile) - os.rename(instRoot + self.configfile, - instRoot + self.configfile + '.rpmsave') + confFile = instRoot + self.configfile + + if os.access (confFile, os.R_OK): + self.perms = os.stat(confFile)[0] & 0777 + lilo.read(confFile) + os.rename(confFile, confFile + ".rpmsave") # Remove any invalid entries that are in the file; we probably # just removed those kernels. -- cgit From b51b0ecf27ac076103484d0c8d00ba86341ac614 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 27 Feb 2009 14:40:00 -0500 Subject: Stop using isys.hardDriveDict. --- booty/bootloaderInfo.py | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index c149a5461..811e8fe69 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -472,20 +472,9 @@ class bootloaderInfo: f.write(" " + arg) f.write("\n") - def createDriveList(self): - # create a drive list that we can use for drive mappings - # XXX has anaconda internals knowledge - import isys - drives = isys.hardDriveDict().keys() - drives.sort(isys.compareDrives) - - # now filter out all of the drives without media present - drives = filter(lambda x: isys.mediaPresent(x), drives) - - return drives - def updateDriveList(self, sortedList=[]): - self._drivelist = self.createDriveList() + self._drivelist = map(lambda x: x.name, filter(lambda x: isys.mediaPresent(x.name), self.storage.disks)) + self._drivelist.sort(isys.compareDrives) # If we're given a sort order, make sure the drives listed in it # are put at the head of the drivelist in that order. All other -- cgit From 1983df2711bef5e8c0f9b5730a15ac665a9f0164 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 14:29:49 -0500 Subject: Use booty.getBootloader instead of going through an intermediary. --- bootloader.py | 4 ---- instdata.py | 4 ++-- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/bootloader.py b/bootloader.py index f12165e9e..b336dab67 100644 --- a/bootloader.py +++ b/bootloader.py @@ -204,10 +204,6 @@ def writeBootloader(anaconda): dosync() -# return instance of the appropriate bootloader for our arch -def getBootloader(): - return booty.getBootloader() - def hasWindows(bl): foundWindows = False for (k,v) in bl.images.getImages().iteritems(): diff --git a/instdata.py b/instdata.py index 990b125b3..e9a66245d 100644 --- a/instdata.py +++ b/instdata.py @@ -30,7 +30,7 @@ import firewall import security import timezone import desktop -import bootloader +import booty import storage import urllib import iutil @@ -75,7 +75,7 @@ class InstallData: if flags.cmdline.has_key("preupgrade"): self.upgrade = True self.storage = storage.Storage(self.anaconda) - self.bootloader = bootloader.getBootloader() + self.bootloader = booty.getBootloader(self.storage) self.upgradeRoot = None self.rootParts = None self.upgradeSwapInfo = None -- cgit From 596ffcad57ec5f51238a6c295cc3eece95388ae4 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 14:32:07 -0500 Subject: Add a storage instance to all bootloaderInfo subclasses. We could pass storage around to all the various functions that will need it, but that's a big mess. It's far easier to just set this when we create the bootloaderInfo class and never worry about it again. --- booty/__init__.py | 18 +++++++++--------- booty/alpha.py | 8 ++++---- booty/bootloaderInfo.py | 12 ++++++++---- booty/ia64.py | 4 ++-- booty/ppc.py | 44 +++++++++++++++++++++----------------------- booty/s390.py | 8 ++++---- booty/sparc.py | 10 +++++----- booty/x86.py | 18 +++++++++--------- 8 files changed, 62 insertions(+), 60 deletions(-) diff --git a/booty/__init__.py b/booty/__init__.py index a5bc97a6a..0b1372ac3 100644 --- a/booty/__init__.py +++ b/booty/__init__.py @@ -21,28 +21,28 @@ from bootloaderInfo import * from bootloader import * # return instance of the appropriate bootloader for our arch -def getBootloader(): +def getBootloader(storage): """Get the bootloader info object for your architecture""" if rhpl.getArch() == 'i386': import x86 - return x86.x86BootloaderInfo() + return x86.x86BootloaderInfo(storage) elif rhpl.getArch() == 'ia64': import ia64 - return ia64.ia64BootloaderInfo() + return ia64.ia64BootloaderInfo(storage) elif rhpl.getArch() == 's390' or rhpl.getArch() == "s390x": import s390 - return s390.s390BootloaderInfo() + return s390.s390BootloaderInfo(storage) elif rhpl.getArch() == "alpha": import alpha - return alpha.alphaBootloaderInfo() + return alpha.alphaBootloaderInfo(storage) elif rhpl.getArch() == "x86_64": import x86 - return x86.x86BootloaderInfo() + return x86.x86BootloaderInfo(storage) elif rhpl.getArch() == "ppc": import pcc - return ppc.ppcBootloaderInfo() + return ppc.ppcBootloaderInfo(storage) elif rhpl.getArch() == "sparc": import sparc - return sparc.sparcBootloaderInfo() + return sparc.sparcBootloaderInfo(storage) else: - return bootloaderInfo() + return bootloaderInfo(storage) diff --git a/booty/alpha.py b/booty/alpha.py index 977e17ea6..e137cdd13 100644 --- a/booty/alpha.py +++ b/booty/alpha.py @@ -17,9 +17,9 @@ class alphaBootloaderInfo(bootloaderInfo): def writeAboot(self, instRoot, bl, kernelList, chainList, defaultDev, justConfig): - rootDevice = storage.fsset.mountpoints["/"] + rootDevice = self.storage.fsset.mountpoints["/"] try: - bootDevice = storage.fsset.mountpoints["/boot"] + bootDevice = self.storage.fsset.mountpoints["/boot"] except KeyError: bootDevice = rootDevice @@ -135,8 +135,8 @@ class alphaBootloaderInfo(bootloaderInfo): self.writeAboot(instRoot, bl, kernelList, chainList, defaultDev, justConfig) - def __init__(self): - bootloaderInfo.__init__(self) + def __init__(self, storage): + bootloaderInfo.__init__(self, storage) self.useGrubVal = 0 self.configfile = "/etc/aboot.conf" # self.kernelLocation is already set to what we need. diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 811e8fe69..e6227b683 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -344,7 +344,7 @@ class bootloaderInfo: lilo.addEntry("timeout", self.timeout or "20", replace = 0) try: - rootDev = storage.fsset.mountpoints["/"] + rootDev = self.storage.fsset.mountpoints["/"] except KeyError: raise RuntimeError, "Installing lilo, but there is no root device" @@ -499,7 +499,7 @@ class bootloaderInfo: self._drivelist = val drivelist = property(_getDriveList, _setDriveList) - def __init__(self): + def __init__(self, storage): self.args = KernelArguments() self.images = BootImages() self.device = None @@ -513,6 +513,7 @@ class bootloaderInfo: self.pure = None self.above1024 = 0 self.timeout = None + self.storage = storage # this has somewhat strange semantics. if 0, act like a normal # "install" case. if 1, update lilo.conf (since grubby won't do that) @@ -628,9 +629,12 @@ class efiBootloaderInfo(bootloaderInfo): self.removeOldEfiEntries(instRoot) self.addNewEfiEntry(instRoot) - def __init__(self, initialize = True): + def __init__(self, storage, initialize = True): if initialize: - bootloaderInfo.__init__(self) + bootloaderInfo.__init__(self, storage) + else: + self.storage = storage + if iutil.isEfi(): self._configdir = "/boot/efi/EFI/redhat" self._configname = "grub.conf" diff --git a/booty/ia64.py b/booty/ia64.py index 174c696d5..2dcee97c0 100644 --- a/booty/ia64.py +++ b/booty/ia64.py @@ -33,7 +33,7 @@ class ia64BootloaderInfo(efiBootloaderInfo): def makeInitrd(self, kernelTag): return "/boot/efi/EFI/redhat/initrd%s.img" % kernelTag - def __init__(self): - efiBootloaderInfo.__init__(self) + def __init__(self, storage): + efiBootloaderInfo.__init__(self, storage) self._configname = "elilo.conf" self._bootloader = "elilo.efi" diff --git a/booty/ppc.py b/booty/ppc.py index bf92e71e3..438813360 100644 --- a/booty/ppc.py +++ b/booty/ppc.py @@ -7,41 +7,39 @@ import iutil import rhpl class ppcBootloaderInfo(bootloaderInfo): - def getBootDevs(self, fs, bl): - import fsset + def getBootDevs(self, bl): + import parted - devs = [] + retval = [] machine = rhpl.getPPCMachine() if machine == 'pSeries': - for entry in fs.entries: - if isinstance(entry.fsystem, fsset.prepbootFileSystem) \ - and entry.format: - devs.append('/dev/%s' % (entry.device.getDevice(),)) + for dev in self.storage.fsset.devices: + if dev.partedFlags[parted.PARTITION_PREP] and not dev.format.exists: + retval.append(dev.path) elif machine == 'PMac': - for entry in fs.entries: - if isinstance(entry.fsystem, fsset.applebootstrapFileSystem) \ - and entry.format: - devs.append('/dev/%s' % (entry.device.getDevice(),)) + for dev in self.storage.fsset.devices: + if dev.format.type == "hfs" and dev.format.bootable and not dev.format.exists: + retval.append(dev.path) - if len(devs) == 0: + if len(retval) == 0: # Try to get a boot device; bplan OF understands ext3 if machine == 'Pegasos' or machine == 'Efika': try: - device = storage.fsset.mountpoints["/boot"] + device = self.storage.fsset.mountpoints["/boot"] except KeyError: try: # Try / if we don't have this we're not going to work - device = storage.fsset.mountpoints["/"] + device = self.storage.fsset.mountpoints["/"] except KeyError: - return devs + return retval - devs.append(device.path) + retval.append(device.path) else: if bl.getDevice(): - devs.append("/dev/%s" % bl.getDevice()) - return devs + retval.append(bl.getDevice().path) + return retval def writeYaboot(self, instRoot, bl, kernelList, chainList, defaultDev, justConfigFile): @@ -49,14 +47,14 @@ class ppcBootloaderInfo(bootloaderInfo): yabootTarget = string.join(self.getBootDevs(bl)) try: - bootDev = storage.fsset.mountpoints["/boot"] + bootDev = self.storage.fsset.mountpoints["/boot"] cf = "/boot/etc/yaboot.conf" cfPath = "" if not os.path.isdir(instRoot + "/boot/etc"): os.mkdir(instRoot + "/boot/etc") except KeyError: - bootDev = storage.fsset.mountpoints["/"] + bootDev = self.storage.fsset.mountpoints["/"] cfPath = "/boot" cf = "/etc/yaboot.conf" @@ -109,7 +107,7 @@ class ppcBootloaderInfo(bootloaderInfo): f.write("\n") - rootDev = storage.fsset.mountpoints["/"] + rootDev = self.storage.fsset.mountpoints["/"] for (label, longlabel, version) in kernelList: kernelTag = "-" + version @@ -176,8 +174,8 @@ class ppcBootloaderInfo(bootloaderInfo): else: self.noKernelsWarn(intf) - def __init__(self): - bootloaderInfo.__init__(self) + def __init__(self, storage): + bootloaderInfo.__init__(self, storage) self.useYabootVal = 1 self.kernelLocation = "/boot" self.configfile = "/etc/yaboot.conf" diff --git a/booty/s390.py b/booty/s390.py index a96ac0033..32d28b463 100644 --- a/booty/s390.py +++ b/booty/s390.py @@ -28,7 +28,7 @@ class s390BootloaderInfo(bootloaderInfo): lilo.delImage(label) try: - rootDev = storage.fsset.mountpoints["/"] + rootDev = self.storage.fsset.mountpoints["/"] except KeyError: raise RuntimeError, "Installing zipl, but there is no root device" @@ -124,7 +124,7 @@ class s390BootloaderInfo(bootloaderInfo): def writeZipl(self, instRoot, bl, kernelList, chainList, defaultDev, justConfigFile): images = bl.images.getImages() - rootDev = storage.fsset.mountpoints["/"] + rootDev = self.storage.fsset.mountpoints["/"] cf = '/etc/zipl.conf' self.perms = 0600 @@ -173,8 +173,8 @@ class s390BootloaderInfo(bootloaderInfo): justConfig | (not self.useZiplVal)) out = self.writeChandevConf(bl, instRoot) - def __init__(self): - bootloaderInfo.__init__(self) + def __init__(self, storage): + bootloaderInfo.__init__(self, storage) self.useZiplVal = 1 # only used on s390 self.kernelLocation = "/boot/" self.configfile = "/etc/zipl.conf" diff --git a/booty/sparc.py b/booty/sparc.py index 1de44927d..086f9786a 100644 --- a/booty/sparc.py +++ b/booty/sparc.py @@ -7,7 +7,7 @@ class sparcBootloaderInfo(bootloaderInfo): chainList, defaultDev, justConfigFile): try: - bootDev = storage.fsset.mountpoints["/boot"] + bootDev = self.storage.fsset.mountpoints["/boot"] mf = '/silo.message' cf = "/boot/silo.conf" @@ -16,7 +16,7 @@ class sparcBootloaderInfo(bootloaderInfo): if not os.path.isdir(instRoot + "/boot"): os.mkdir(instRoot + "/boot") except KeyError: - bootDev = storage.fsset.mountpoints["/"] + bootDev = self.storage.fsset.mountpoints["/"] cf = "/etc/silo.conf" mfdir = '/etc' @@ -45,7 +45,7 @@ class sparcBootloaderInfo(bootloaderInfo): f.write("default=%s\n" % (kernelList[0][0],)) f.write("\n") - rootDev = storage.fsset.mountpoints["/"] + rootDev = self.storage.fsset.mountpoints["/"] for (label, longlabel, version) in kernelList: kernelTag = "-" + version @@ -118,8 +118,8 @@ class sparcBootloaderInfo(bootloaderInfo): else: self.noKernelsWarn(intf) - def __init__(self): - bootloaderInfo.__init__(self) + def __init__(self, storage): + bootloaderInfo.__init__(self, storage) self.useSiloVal = 1 self.kernelLocation = "/boot" self._configdir = "/etc" diff --git a/booty/x86.py b/booty/x86.py index 3046cf7e5..d14cbc223 100644 --- a/booty/x86.py +++ b/booty/x86.py @@ -130,7 +130,7 @@ class x86BootloaderInfo(efiBootloaderInfo): defaultDev, justConfigFile): images = bl.images.getImages() - rootDev = storage.fsset.mountpoints["/"] + rootDev = self.storage.fsset.mountpoints["/"] # XXX old config file should be read here for upgrade @@ -161,7 +161,7 @@ class x86BootloaderInfo(efiBootloaderInfo): "after making changes to this file\n") try: - bootDev = storage.fsset.mountpoints["/boot"] + bootDev = self.storage.fsset.mountpoints["/boot"] grubPath = "/grub" cfPath = "/" f.write("# NOTICE: You have a /boot partition. This means " @@ -169,7 +169,7 @@ class x86BootloaderInfo(efiBootloaderInfo): f.write("# all kernel and initrd paths are relative " "to /boot/, eg.\n") except KeyError: - bootDev = storage.fsset.mountpoints["/"] + bootDev = self.storage.fsset.mountpoints["/"] grubPath = "/boot/grub" cfPath = "/boot/" f.write("# NOTICE: You do not have a /boot partition. " @@ -454,11 +454,11 @@ class x86BootloaderInfo(efiBootloaderInfo): # so we have to do shenanigans to get updated grub installed... # steal some more code above try: - bootDev = storage.fsset.mountpoints["/boot"] + bootDev = self.storage.fsset.mountpoints["/boot"] grubPath = "/grub" cfPath = "/" except KeyError: - bootDev = storage.fsset.mountpoints["/"] + bootDev = self.storage.fsset.mountpoints["/"] grubPath = "/boot/grub" cfPath = "/boot/" @@ -500,7 +500,7 @@ class x86BootloaderInfo(efiBootloaderInfo): cmds.append(cmd) if not justConfigFile: - self.runGrubInstall(instRoot, bootDev cmds, cfPath) + self.runGrubInstall(instRoot, bootDev, cmds, cfPath) return "" @@ -552,9 +552,9 @@ class x86BootloaderInfo(efiBootloaderInfo): return args - def __init__(self): - bootloaderInfo.__init__(self) - efiBootloaderInfo.__init__(self, initialize=False) + def __init__(self, storage): + bootloaderInfo.__init__(self, storage) + efiBootloaderInfo.__init__(self, storage, initialize=False) self._configdir = "/boot/grub" self._configname = "grub.conf" -- cgit From 6866ce610d95fdccbe214ccac3c90d68eeb71494 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 14:33:50 -0500 Subject: Move the isys import higher up. --- booty/bootloaderInfo.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index e6227b683..5509c7681 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -31,6 +31,7 @@ from rhpl.translate import _, N_ from flags import flags from fsset import getDiskPart import iutil +import isys from product import * import booty @@ -59,7 +60,6 @@ def checkForBootBlock(device): # there's no guarantee that data is written to the disk and grub # reads both the filesystem and the disk. suck. def syncDataToDisk(dev, mntpt, instRoot = "/"): - import isys isys.sync() isys.sync() isys.sync() -- cgit From 4b4588cce875b134b8d2c944632f7e2343d62431 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 3 Mar 2009 14:38:39 -0500 Subject: Use anaconda's provided rootDevice instead of performing another lookup. --- booty/alpha.py | 2 +- booty/bootloaderInfo.py | 12 +++--------- booty/ppc.py | 11 ++++------- booty/s390.py | 7 ++----- booty/sparc.py | 4 ++-- booty/x86.py | 6 +++--- 6 files changed, 15 insertions(+), 27 deletions(-) diff --git a/booty/alpha.py b/booty/alpha.py index e137cdd13..29b284099 100644 --- a/booty/alpha.py +++ b/booty/alpha.py @@ -17,7 +17,7 @@ class alphaBootloaderInfo(bootloaderInfo): def writeAboot(self, instRoot, bl, kernelList, chainList, defaultDev, justConfig): - rootDevice = self.storage.fsset.mountpoints["/"] + rootDevice = self.storage.fsset.rootDevice try: bootDevice = self.storage.fsset.mountpoints["/boot"] except KeyError: diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 5509c7681..0a32880ed 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -251,12 +251,9 @@ class BootImages: if self.partedFlags[parted.PARTITION_BOOT]: retval.append((part, type)) - try: - rootDevice = storage.fsset.mountpoints["/"] - except KeyError: - rootDevice = None + rootDevice = storage.fsset.rootDevice - if not rootDevice or not rootDevice.format.bootable: + if not rootDevice or not rootDevice.format: raise ValueError, ("Trying to pick boot devices but do not have a " "sane root partition. Aborting install.") @@ -343,10 +340,7 @@ class bootloaderInfo: lilo.addEntry("prompt", replace = 0) lilo.addEntry("timeout", self.timeout or "20", replace = 0) - try: - rootDev = self.storage.fsset.mountpoints["/"] - except KeyError: - raise RuntimeError, "Installing lilo, but there is no root device" + rootDev = self.storage.fsset.rootDevice if rootDev == defaultDev: lilo.addEntry("default", kernelList[0][0]) diff --git a/booty/ppc.py b/booty/ppc.py index 438813360..23662b91f 100644 --- a/booty/ppc.py +++ b/booty/ppc.py @@ -28,11 +28,8 @@ class ppcBootloaderInfo(bootloaderInfo): try: device = self.storage.fsset.mountpoints["/boot"] except KeyError: - try: - # Try / if we don't have this we're not going to work - device = self.storage.fsset.mountpoints["/"] - except KeyError: - return retval + # Try / if we don't have this we're not going to work + device = self.storage.fsset.rootDevice retval.append(device.path) else: @@ -54,7 +51,7 @@ class ppcBootloaderInfo(bootloaderInfo): if not os.path.isdir(instRoot + "/boot/etc"): os.mkdir(instRoot + "/boot/etc") except KeyError: - bootDev = self.storage.fsset.mountpoints["/"] + bootDevice = self.storage.fsset.rootDevice cfPath = "/boot" cf = "/etc/yaboot.conf" @@ -107,7 +104,7 @@ class ppcBootloaderInfo(bootloaderInfo): f.write("\n") - rootDev = self.storage.fsset.mountpoints["/"] + rootDev = self.storage.fsset.rootDevice for (label, longlabel, version) in kernelList: kernelTag = "-" + version diff --git a/booty/s390.py b/booty/s390.py index 32d28b463..95ac140a8 100644 --- a/booty/s390.py +++ b/booty/s390.py @@ -27,10 +27,7 @@ class s390BootloaderInfo(bootloaderInfo): if not os.access(instRoot + sl.getPath(), os.R_OK): lilo.delImage(label) - try: - rootDev = self.storage.fsset.mountpoints["/"] - except KeyError: - raise RuntimeError, "Installing zipl, but there is no root device" + rootDev = self.storage.fsset.rootDevice if rootDev == defaultDev: lilo.addEntry("default", kernelList[0][0]) @@ -124,7 +121,7 @@ class s390BootloaderInfo(bootloaderInfo): def writeZipl(self, instRoot, bl, kernelList, chainList, defaultDev, justConfigFile): images = bl.images.getImages() - rootDev = self.storage.fsset.mountpoints["/"] + rootDev = self.storage.fsset.rootDevice cf = '/etc/zipl.conf' self.perms = 0600 diff --git a/booty/sparc.py b/booty/sparc.py index 086f9786a..1eb53297f 100644 --- a/booty/sparc.py +++ b/booty/sparc.py @@ -16,7 +16,7 @@ class sparcBootloaderInfo(bootloaderInfo): if not os.path.isdir(instRoot + "/boot"): os.mkdir(instRoot + "/boot") except KeyError: - bootDev = self.storage.fsset.mountpoints["/"] + bootDev = self.storage.fsset.rootDevice cf = "/etc/silo.conf" mfdir = '/etc' @@ -45,7 +45,7 @@ class sparcBootloaderInfo(bootloaderInfo): f.write("default=%s\n" % (kernelList[0][0],)) f.write("\n") - rootDev = self.storage.fsset.mountpoints["/"] + rootDev = self.storage.fsset.rootDevice for (label, longlabel, version) in kernelList: kernelTag = "-" + version diff --git a/booty/x86.py b/booty/x86.py index d14cbc223..72fcdb9f1 100644 --- a/booty/x86.py +++ b/booty/x86.py @@ -130,7 +130,7 @@ class x86BootloaderInfo(efiBootloaderInfo): defaultDev, justConfigFile): images = bl.images.getImages() - rootDev = self.storage.fsset.mountpoints["/"] + rootDev = self.storage.fsset.rootDevice # XXX old config file should be read here for upgrade @@ -169,7 +169,7 @@ class x86BootloaderInfo(efiBootloaderInfo): f.write("# all kernel and initrd paths are relative " "to /boot/, eg.\n") except KeyError: - bootDev = self.storage.fsset.mountpoints["/"] + bootDev = self.storage.fsset.rootDevice grubPath = "/boot/grub" cfPath = "/boot/" f.write("# NOTICE: You do not have a /boot partition. " @@ -458,7 +458,7 @@ class x86BootloaderInfo(efiBootloaderInfo): grubPath = "/grub" cfPath = "/" except KeyError: - bootDev = self.storage.fsset.mountpoints["/"] + bootDev = self.storage.fsset.rootDevice grubPath = "/boot/grub" cfPath = "/boot/" -- cgit From 4745aabcc0e3617aab9d734e7b3b160c1348d1f4 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 3 Mar 2009 15:02:02 -0500 Subject: We're not even using the results of getImages here. --- booty/s390.py | 3 --- booty/x86.py | 3 +-- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/booty/s390.py b/booty/s390.py index 95ac140a8..5444c5157 100644 --- a/booty/s390.py +++ b/booty/s390.py @@ -6,8 +6,6 @@ import iutil class s390BootloaderInfo(bootloaderInfo): def getBootloaderConfig(self, instRoot, bl, kernelList, chainList, defaultDev): - images = bl.images.getImages() - # on upgrade read in the lilo config file lilo = LiloConfigFile () self.perms = 0600 @@ -120,7 +118,6 @@ class s390BootloaderInfo(bootloaderInfo): def writeZipl(self, instRoot, bl, kernelList, chainList, defaultDev, justConfigFile): - images = bl.images.getImages() rootDev = self.storage.fsset.rootDevice cf = '/etc/zipl.conf' diff --git a/booty/x86.py b/booty/x86.py index 72fcdb9f1..8bb77579f 100644 --- a/booty/x86.py +++ b/booty/x86.py @@ -128,8 +128,7 @@ class x86BootloaderInfo(efiBootloaderInfo): def writeGrub(self, instRoot, bl, kernelList, chainList, defaultDev, justConfigFile): - - images = bl.images.getImages() + rootDev = self.storage.fsset.rootDevice # XXX old config file should be read here for upgrade -- cgit From 7f94ffba398ac4e6db7020e94cb5383cc03b8e89 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 12:24:08 -0500 Subject: Move BootyNoKernelWarning into __init__.py and use it. Also, this gets rid of the goofy self.noKernelsWarn method which doesn't serve any useful purpose now that booty's part of anaconda. --- bootloader.py | 2 +- booty/__init__.py | 7 +++++++ booty/alpha.py | 3 ++- booty/bootloaderInfo.py | 16 +--------------- booty/ia64.py | 3 ++- booty/ppc.py | 3 ++- booty/sparc.py | 3 ++- booty/x86.py | 3 ++- 8 files changed, 19 insertions(+), 21 deletions(-) diff --git a/bootloader.py b/bootloader.py index b336dab67..9cc9cd62b 100644 --- a/bootloader.py +++ b/bootloader.py @@ -193,7 +193,7 @@ def writeBootloader(anaconda): justConfigFile, anaconda.intf) if not justConfigFile: w.pop() - except bootloaderInfo.BootyNoKernelWarning: + except booty.BootyNoKernelWarning: if not justConfigFile: w.pop() if anaconda.intf: diff --git a/booty/__init__.py b/booty/__init__.py index 0b1372ac3..328128fdf 100644 --- a/booty/__init__.py +++ b/booty/__init__.py @@ -20,6 +20,13 @@ import rhpl from bootloaderInfo import * from bootloader import * +class BootyNoKernelWarning: + def __init__ (self, value=""): + self.value = value + + def __str__ (self): + return self.value + # return instance of the appropriate bootloader for our arch def getBootloader(storage): """Get the bootloader info object for your architecture""" diff --git a/booty/alpha.py b/booty/alpha.py index 29b284099..58008bea9 100644 --- a/booty/alpha.py +++ b/booty/alpha.py @@ -1,6 +1,7 @@ import os import iutil +from booty import BootyNoKernelWarning from bootloaderInfo import * import fsset @@ -130,7 +131,7 @@ class alphaBootloaderInfo(bootloaderInfo): def write(self, instRoot, bl, kernelList, chainList, defaultDev, justConfig, intf): if len(kernelList) < 1: - self.noKernelsWarn(intf) + raise BootyNoKernelWarning self.writeAboot(instRoot, bl, kernelList, chainList, defaultDev, justConfig) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 0a32880ed..ec21d2b73 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -98,13 +98,6 @@ def getRootDevName(initrd, rootDevice): except: return rootDevice.path -class BootyNoKernelWarning: - def __init__ (self, value=""): - self.value = value - - def __str__ (self): - return self.value - class KernelArguments: def get(self): @@ -434,17 +427,10 @@ class bootloaderInfo: defaultDev) config.write(instRoot + self.configfile, perms = self.perms) else: - self.noKernelsWarn(intf) + raise booty.BootyNoKernelWarning return "" - # XXX in the future we also should do some validation on the config - # file that's already there - # XXX concept of the intf isn't very well defined outside of anaconda... - # probably should just pass back up an error - def noKernelsWarn(self, intf): - raise BootyNoKernelWarning - def getArgList(self): args = [] diff --git a/booty/ia64.py b/booty/ia64.py index 2dcee97c0..013545fe6 100644 --- a/booty/ia64.py +++ b/booty/ia64.py @@ -1,3 +1,4 @@ +from booty import BootyNoKernelWarning from bootloaderInfo import * class ia64BootloaderInfo(efiBootloaderInfo): @@ -25,7 +26,7 @@ class ia64BootloaderInfo(efiBootloaderInfo): out = self.writeLilo(instRoot, bl, kernelList, chainList, defaultDev, justConfig) else: - self.noKernelsWarn(intf) + raise BootyNoKernelWarning self.removeOldEfiEntries(instRoot) self.addNewEfiEntry(instRoot) diff --git a/booty/ppc.py b/booty/ppc.py index 23662b91f..5092c104d 100644 --- a/booty/ppc.py +++ b/booty/ppc.py @@ -1,6 +1,7 @@ import string import os +from booty import BootyNoKernelWarning from bootloaderInfo import * import fsset import iutil @@ -169,7 +170,7 @@ class ppcBootloaderInfo(bootloaderInfo): out = self.writeYaboot(instRoot, bl, kernelList, chainList, defaultDev, justConfig) else: - self.noKernelsWarn(intf) + raise BootyNoKernelWarning def __init__(self, storage): bootloaderInfo.__init__(self, storage) diff --git a/booty/sparc.py b/booty/sparc.py index 1eb53297f..f214a98e2 100644 --- a/booty/sparc.py +++ b/booty/sparc.py @@ -1,5 +1,6 @@ import os +from booty import BootyNoKernelWarning from bootloaderInfo import * class sparcBootloaderInfo(bootloaderInfo): @@ -116,7 +117,7 @@ class sparcBootloaderInfo(bootloaderInfo): self.writeSilo(instRoot, bl, kernelList, chainList, defaultDev, justConfig) else: - self.noKernelsWarn(intf) + raise BootyNoKernelWarning def __init__(self, storage): bootloaderInfo.__init__(self, storage) diff --git a/booty/x86.py b/booty/x86.py index 8bb77579f..2ef765fed 100644 --- a/booty/x86.py +++ b/booty/x86.py @@ -1,6 +1,7 @@ import os import string +from booty import BootyNoKernelWarning from bootloaderInfo import * import checkbootloader import fsset @@ -528,7 +529,7 @@ class x86BootloaderInfo(efiBootloaderInfo): return if len(kernelList) < 1: - self.noKernelsWarn(intf) + raise BootyNoKernelWarning out = self.writeGrub(instRoot, bl, kernelList, chainList, defaultDev, -- cgit From d899dad616076a9c6183306c4cdcf442405c3190 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 12:42:31 -0500 Subject: bootloader.write takes fewer arguments now, so clean up arg passing. --- bootloader.py | 6 ++---- booty/alpha.py | 2 +- booty/bootloaderInfo.py | 2 +- booty/ia64.py | 2 +- booty/ppc.py | 2 +- booty/s390.py | 2 +- booty/sparc.py | 2 +- booty/x86.py | 2 +- 8 files changed, 9 insertions(+), 11 deletions(-) diff --git a/bootloader.py b/bootloader.py index 9cc9cd62b..313ecdff7 100644 --- a/bootloader.py +++ b/bootloader.py @@ -186,11 +186,9 @@ def writeBootloader(anaconda): dosync() try: - anaconda.id.bootloader.write(anaconda.rootPath, anaconda.id.storage, - anaconda.id.bootloader, - anaconda.id.instLanguage, + anaconda.id.bootloader.write(anaconda.rootPath, anaconda.id.bootloader, kernelList, otherList, defaultDev, - justConfigFile, anaconda.intf) + justConfigFile) if not justConfigFile: w.pop() except booty.BootyNoKernelWarning: diff --git a/booty/alpha.py b/booty/alpha.py index 58008bea9..03f0653c9 100644 --- a/booty/alpha.py +++ b/booty/alpha.py @@ -129,7 +129,7 @@ class alphaBootloaderInfo(bootloaderInfo): def write(self, instRoot, bl, kernelList, chainList, - defaultDev, justConfig, intf): + defaultDev, justConfig): if len(kernelList) < 1: raise BootyNoKernelWarning diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index ec21d2b73..dd94f4024 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -420,7 +420,7 @@ class bootloaderInfo: return lilo def write(self, instRoot, bl, kernelList, chainList, - defaultDev, justConfig, intf = None): + defaultDev, justConfig): if len(kernelList) >= 1: config = self.getBootloaderConfig(instRoot, bl, kernelList, chainList, diff --git a/booty/ia64.py b/booty/ia64.py index 013545fe6..b9646d3a5 100644 --- a/booty/ia64.py +++ b/booty/ia64.py @@ -21,7 +21,7 @@ class ia64BootloaderInfo(efiBootloaderInfo): return "" def write(self, instRoot, bl, kernelList, chainList, - defaultDev, justConfig, intf): + defaultDev, justConfig): if len(kernelList) >= 1: out = self.writeLilo(instRoot, bl, kernelList, chainList, defaultDev, justConfig) diff --git a/booty/ppc.py b/booty/ppc.py index 5092c104d..af90ef0bd 100644 --- a/booty/ppc.py +++ b/booty/ppc.py @@ -165,7 +165,7 @@ class ppcBootloaderInfo(bootloaderInfo): self.password = val def write(self, instRoot, bl, kernelList, chainList, - defaultDev, justConfig, intf): + defaultDev, justConfig): if len(kernelList) >= 1: out = self.writeYaboot(instRoot, bl, kernelList, chainList, defaultDev, justConfig) diff --git a/booty/s390.py b/booty/s390.py index 5444c5157..2c3f8a37e 100644 --- a/booty/s390.py +++ b/booty/s390.py @@ -161,7 +161,7 @@ class s390BootloaderInfo(bootloaderInfo): return "" def write(self, instRoot, bl, kernelList, chainList, - defaultDev, justConfig, intf): + defaultDev, justConfig): out = self.writeZipl(instRoot, bl, kernelList, chainList, defaultDev, justConfig | (not self.useZiplVal)) diff --git a/booty/sparc.py b/booty/sparc.py index f214a98e2..46c9cdbb7 100644 --- a/booty/sparc.py +++ b/booty/sparc.py @@ -112,7 +112,7 @@ class sparcBootloaderInfo(bootloaderInfo): self.password = val def write(self, instRoot, bl, kernelList, chainList, - defaultDev, justConfig, intf): + defaultDev, justConfig): if len(kernelList) >= 1: self.writeSilo(instRoot, bl, kernelList, chainList, defaultDev, justConfig) diff --git a/booty/x86.py b/booty/x86.py index 2ef765fed..a84519b13 100644 --- a/booty/x86.py +++ b/booty/x86.py @@ -517,7 +517,7 @@ class x86BootloaderInfo(efiBootloaderInfo): f.close() def write(self, instRoot, bl, kernelList, chainList, - defaultDev, justConfig, intf): + defaultDev, justConfig): if self.timeout is None and chainList: self.timeout = 5 -- cgit From 5addce7900be1341f6c22e5bd1af150c07002c95 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 13:28:38 -0500 Subject: Adapt getPhysicalDevices to the new storage code. --- booty/x86.py | 50 ++++++++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/booty/x86.py b/booty/x86.py index a84519b13..b9c12cdd3 100644 --- a/booty/x86.py +++ b/booty/x86.py @@ -46,15 +46,9 @@ class x86BootloaderInfo(efiBootloaderInfo): # Accepted values for "device" are raid1 md devices (i.e. "md0"), # physical disks ("hda"), and real partitions on physical disks # ("hda1"). Volume groups/logical volumes are not accepted. - # - # XXX this has internal anaconda-ish knowledge. ick. - import isys - import lvm - - if string.split(device, '/', 1)[0] in map (lambda vg: vg[0], - lvm.vglist()): + if string.split(device, '/', 1)[0] in map (lambda x: x.name, self.storage.lvs + self.storage.vgs): return [] - + if device.startswith("mapper/luks-"): return [] @@ -316,26 +310,26 @@ class x86BootloaderInfo(efiBootloaderInfo): if os.access(instRoot + "/boot/grub/device.map", os.R_OK): os.rename(instRoot + "/boot/grub/device.map", instRoot + "/boot/grub/device.map.rpmsave") - if 1: # not os.access(instRoot + "/boot/grub/device.map", os.R_OK): - f = open(instRoot + "/boot/grub/device.map", "w+") - f.write("# this device map was generated by anaconda\n") - devs = usedDevs.keys() - usedDevs = {} - for dev in devs: - drive = fsset.getDiskPart(dev)[0] - if usedDevs.has_key(drive): - continue - usedDevs[drive] = 1 - devs = usedDevs.keys() - devs.sort() - for drive in devs: - # XXX hack city. If they're not the sort of thing that'll - # be in the device map, they shouldn't still be in the list. - if not drive.startswith('md'): - f.write("(%s) /dev/%s\n" % (self.grubbyDiskName(drive), - drive)) - f.close() - + + f = open(instRoot + "/boot/grub/device.map", "w+") + f.write("# this device map was generated by anaconda\n") + devs = usedDevs.keys() + usedDevs = {} + for dev in devs: + drive = fsset.getDiskPart(dev)[0] + if usedDevs.has_key(drive): + continue + usedDevs[drive] = 1 + devs = usedDevs.keys() + devs.sort() + for drive in devs: + # XXX hack city. If they're not the sort of thing that'll + # be in the device map, they shouldn't still be in the list. + if not drive.startswith('md'): + f.write("(%s) /dev/%s\n" % (self.grubbyDiskName(drive), + drive)) + f.close() + sysconf = '/etc/sysconfig/grub' if os.access (instRoot + sysconf, os.R_OK): self.perms = os.stat(instRoot + sysconf)[0] & 0777 -- cgit From 9fd282a9e2fb3a63db6b86c5cf07b1fb92e5a9cc Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 13:44:44 -0500 Subject: Fix passing a device vs. passing a device's name in several more places. --- booty/alpha.py | 2 +- booty/bootloaderInfo.py | 2 +- booty/ppc.py | 2 +- booty/s390.py | 2 +- booty/sparc.py | 2 +- booty/x86.py | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/booty/alpha.py b/booty/alpha.py index 03f0653c9..b5ed98a47 100644 --- a/booty/alpha.py +++ b/booty/alpha.py @@ -86,7 +86,7 @@ class alphaBootloaderInfo(bootloaderInfo): if os.path.isfile(instRoot + initrd): f.write(" initrd=%sinitrd%s.img" %(kernelPath, kernelTag)) - realroot = getRootDevName(instRoot+initrd, rootDevice.path) + realroot = getRootDevName(instRoot+initrd, rootDevice) f.write(" root=%s" %(realroot,)) args = self.args.get() diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index dd94f4024..4d5c343cf 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -361,7 +361,7 @@ class bootloaderInfo: sl.addEntry("read-only") append = "%s" %(self.args.get(),) - realroot = getRootDevName(instRoot+initrd, rootDev.path) + realroot = getRootDevName(instRoot+initrd, rootDev) if rootIsDevice(realroot): sl.addEntry("root", rootDev.path) else: diff --git a/booty/ppc.py b/booty/ppc.py index af90ef0bd..d653052ce 100644 --- a/booty/ppc.py +++ b/booty/ppc.py @@ -121,7 +121,7 @@ class ppcBootloaderInfo(bootloaderInfo): append = "%s" %(self.args.get(),) - realroot = getRootDevName(instRoot+initrd, rootDev.path) + realroot = getRootDevName(instRoot+initrd, rootDev) if rootIsDevice(realroot): f.write("\troot=%s\n" %(realroot,)) else: diff --git a/booty/s390.py b/booty/s390.py index 2c3f8a37e..655d028b8 100644 --- a/booty/s390.py +++ b/booty/s390.py @@ -144,7 +144,7 @@ class s390BootloaderInfo(bootloaderInfo): if os.access (instRoot + initrd, os.R_OK): f.write('\tramdisk=%sinitrd%s.img\n' %(self.kernelLocation, kernelTag)) - realroot = getRootDevName(instRoot+initrd, rootDev.path) + realroot = getRootDevName(instRoot+initrd, rootDev) f.write('\tparameters="root=%s' %(realroot,)) if bl.args.get(): f.write(' %s' % (bl.args.get())) diff --git a/booty/sparc.py b/booty/sparc.py index 46c9cdbb7..5ea45eebe 100644 --- a/booty/sparc.py +++ b/booty/sparc.py @@ -62,7 +62,7 @@ class sparcBootloaderInfo(bootloaderInfo): append = "%s" % (self.args.get(),) - realroot = getRootDevName(instRoot+initrd, rootDev.path) + realroot = getRootDevName(instRoot+initrd, rootDev) if rootIsDevice(realroot): f.write("\troot=%s\n" % (realroot,)) else: diff --git a/booty/x86.py b/booty/x86.py index b9c12cdd3..2e01935d3 100644 --- a/booty/x86.py +++ b/booty/x86.py @@ -171,7 +171,7 @@ class x86BootloaderInfo(efiBootloaderInfo): f.write("# all kernel and initrd paths are relative " "to /, eg.\n") - bootDevs = self.getPhysicalDevices(bootDev) + bootDevs = self.getPhysicalDevices(bootDev.name) f.write('# root %s\n' % self.grubbyPartitionName(bootDevs[0])) f.write("# kernel %svmlinuz-version ro root=%s\n" % (cfPath, rootDev.path)) @@ -233,7 +233,7 @@ class x86BootloaderInfo(efiBootloaderInfo): f.write('title %s (%s)\n' % (longlabel, version)) f.write('\troot %s\n' % self.grubbyPartitionName(bootDevs[0])) - realroot = getRootDevName(instRoot+initrd, rootDev.path) + realroot = getRootDevName(instRoot+initrd, rootDev) realroot = " root=%s" %(realroot,) if version.endswith("xen0") or (version.endswith("xen") and not os.path.exists("/proc/xen")): @@ -304,7 +304,7 @@ class x86BootloaderInfo(efiBootloaderInfo): except: pass - for dev in self.getPhysicalDevices(rootDev) + bootDevs: + for dev in self.getPhysicalDevices(rootDev.name) + bootDevs: usedDevs[dev] = 1 if os.access(instRoot + "/boot/grub/device.map", os.R_OK): -- cgit From 16022f04951d684a377a6b71e9870eb4ac4a3b2c Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 16:23:41 -0600 Subject: A couple of little fixes to the partedPartition property. --- storage/devices.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 320e23db5..6071cc00c 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -821,7 +821,7 @@ class PartitionDevice(StorageDevice): ppart = None if self._partedPartition and self.disk and \ self.disk.partedDisk == self._partedPartition.disk: - ppart = self._partedPartition + ppart = self._partedPartition elif self.disk: pdisk = self.disk.partedDisk ppart = pdisk.getPartitionByPath(self.path) @@ -842,8 +842,13 @@ class PartitionDevice(StorageDevice): log.debug("device %s new partedPartition %s has path %s" % (self.name, partition, path)) - self._partedPartition = partition - self._name = partition.getDeviceNodeName() + + if partition is None: + self._partedPartition = None + # no need to clobber our name + else: + self._partedPartition = partition + self._name = partition.getDeviceNodeName() partedPartition = property(lambda d: d._getPartedPartition(), lambda d,p: d._setPartedPartition(p)) @@ -1041,6 +1046,7 @@ class PartitionDevice(StorageDevice): return disk def _setDisk(self, disk): + log_method_call(self, self.name, old=self.disk, new=disk) if self.disk: self.disk.removeChild() -- cgit From e983156de4195706c505e448bf982dc97a7cf7ea Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 16:40:06 -0600 Subject: Fix inaccuracies in growPartitions and work around some parted weirdness. After calling parted.Disk.addPartition(p), the disk's version of the partition has a different size than the one you added. Whatever, parted. --- storage/partitioning.py | 59 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 41 insertions(+), 18 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index 697fe0629..6bb0333c8 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -685,9 +685,14 @@ def allocatePartitions(disks, partitions): constraint=disk.device.getConstraint()) # constraint=parted.Constraint(device=disk.device)) log.debug("created partition %s of %dMB and added it to %s" % (partition.getDeviceNodeName(), partition.getSize(), disk)) + + # this one sets the name _part.partedPartition = partition _part.disk = _disk + # parted modifies the partition in the process of adding it to + # the disk, so we need to grab the latest version... + _part.partedPartition = disk.getPartitionByPath(_part.path) def growPartitions(disks, partitions): """ Grow all growable partition requests. @@ -730,6 +735,9 @@ def growPartitions(disks, partitions): for disk in disks: log.debug("growing requests on %s" % disk.name) + for p in disk.partedDisk.partitions: + log.debug(" %s: %s (%dMB)" % (disk.name, p.getDeviceNodeName(), + p.getSize())) sectorSize = disk.partedDisk.device.physicalSectorSize # get a list of free space regions on the disk free = disk.partedDisk.getFreeSpaceRegions() @@ -740,6 +748,8 @@ def growPartitions(disks, partitions): # sort the free regions in decreasing order of size free.sort(key=lambda r: r.length, reverse=True) disk_free = reduce(lambda x,y: x + y, [f.length for f in free]) + log.debug("total free: %d sectors ; largest: %d sectors (%dMB)" + % (disk_free, free[0].length, free[0].getSize())) # make a list of partitions currently allocated on this disk # -- they're already sorted @@ -750,21 +760,29 @@ def growPartitions(disks, partitions): # part.disk.name)) if part.disk == disk: growable.append(part) - disk_total += (part.req_size * (1024 * 1024)) / sectorSize + disk_total += part.partedPartition.geometry.length + log.debug("add %s (%dMB/%d sectors) to growable total" + % (part.name, part.partedPartition.getSize(), + part.partedPartition.geometry.length)) + log.debug("growable total is now %d sectors" % disk_total) # now we loop through the partitions... for part in growable: # calculate max number of sectors this request can grow - sectors = (part.req_size * (1024 * 1024)) / sectorSize - share = float(sectors) / float(disk_total) - max_grow = share * disk_free - max_mb = (max_grow * sectorSize) / (1024 * 1024) + req_sectors = part.partedPartition.geometry.length + share = float(req_sectors) / float(disk_total) + max_grow = (share * disk_free) + max_sectors = req_sectors + max_grow + max_mb = (max_sectors * sectorSize) / (1024 * 1024) log.debug("%s: base_size=%dMB, max_size=%sMB" % (part.name, part.req_base_size, part.req_max_size)) + log.debug("%s: current_size=%dMB (%d sectors)" % (part.name, + part.partedPartition.getSize(), + part.partedPartition.geometry.length)) log.debug("%s: %dMB (%d sectors, or %d%% of %d)" % (part.name, max_mb, - max_grow, + max_sectors, share * 100, disk_free)) @@ -773,23 +791,26 @@ def growPartitions(disks, partitions): if part.req_max_size: log.debug("max_size: %dMB" % part.req_max_size) # FIXME: round down to nearest cylinder boundary - max_sect = (part.req_max_size * (1024 * 1024)) / sectorSize - if max_sect < sectors + max_grow: - max_grow = (max_sect - sectors) + req_max_sect = (part.req_max_size * (1024 * 1024)) / sectorSize + if req_max_sect < max_sectors: + max_grow -= (max_sectors - req_max_sect) + max_sectors = req_sectors + max_grow # don't grow beyond the resident filesystem's max size if part.format.maxSize > 0: log.debug("format maxsize: %dMB" % part.format.maxSize) # FIXME: round down to nearest cylinder boundary - max_sect = (part.format.maxSize * (1024 * 1024)) / sectorSize - if max_sect < sectors + max_grow: - max_grow = (max_sect - sectors) + fs_max_sect = (part.format.maxSize * (1024 * 1024)) / sectorSize + if fs_max_sect < max_sectors: + max_grow -= (max_sectors - fs_max_sect) + max_sectors = req_sectors + max_grow # we can only grow as much as the largest free region on the disk if free[0].length < max_grow: log.debug("largest free region: %d sectors (%dMB)" % (free[0].length, free[0].getSize())) # FIXME: round down to nearest cylinder boundary max_grow = free[0].length + max_sectors = req_sectors + max_grow # Now, we try to grow this partition as close to max_grow # sectors as we can. @@ -797,8 +818,6 @@ def growPartitions(disks, partitions): # We could call allocatePartitions after modifying this # request and saving the original value of part.req_size, # or we could try to use disk.maximizePartition(). - req_sectors = (part.req_size * 1024 * 1024) / sectorSize - max_sectors = req_sectors + max_grow max_size = (max_sectors * sectorSize) / (1024 * 1024) orig_size = part.req_size # try the max size to begin with @@ -816,14 +835,15 @@ def growPartitions(disks, partitions): log.debug("starting binary search: size=%d max_size=%d" % (part.req_size, max_size)) count = 0 op_func = add - increment = max_grow + increment = max_sectors last_good_size = part.req_size last_outcome = None - while part.req_size < max_size and count < 3: + while (part.partedPartition.geometry.length < max_sectors and + count < 3): last_size = part.req_size increment /= 2 - sectors = op_func(sectors, increment) - part.req_size = (sectors * sectorSize) / (1024 * 1024) + req_sectors = op_func(req_sectors, increment) + part.req_size = (req_sectors * sectorSize) / (1024 * 1024) log.debug("attempting size=%dMB" % part.req_size) count += 1 try: @@ -863,6 +883,9 @@ def growPartitions(disks, partitions): constraint.maxSize = max_sect disk.partedDisk.maximizePartition(part.partedPartition, constraint) + log.debug("grew partition %s to %dMB" % (part.name, + part.partedPartition.getSize())) + log.debug("the disk's copy is %dMB" % disk.partedDisk.getPartitionByPath(part.path).getSize()) # reset all requests to their original requested size for part in partitions: -- cgit From f5071319ad21037333be08c0a43b9a42b24e2c13 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 17:05:27 -0600 Subject: Add a "removable" property to StorageDevice. This is mostly for use by Storage.exceptionDisks. --- storage/devices.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/storage/devices.py b/storage/devices.py index 6071cc00c..27d9a1f1e 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -555,6 +555,13 @@ class StorageDevice(Device): #for parent in self.parents: # parent.removeChild() + @property + def removable(self): + devpath = os.path.normpath("/sys/%s" % self.sysfsPath) + remfile = os.path.normpath("%s/removable" % devpath) + return (self.sysfsPath and os.path.exists(devpath) and + os.access(remfile, os.R_OK) and + open(remfile).readline().strip() == "1") class DiskDevice(StorageDevice): """ A disk """ -- cgit From 4503335c6f5d33bcb236da1757123dde73a842bc Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 18:00:21 -0500 Subject: Make kickstart work against the new partitioning code. This doesn't atually mean kickstart allows you to specify partitions and clearpart. However, you can leave out partitioning information from your kickstart file, specify it from the UI, and get an install to complete. --- installclasses/fedora.py | 9 ++++----- installclasses/rhel.py | 9 ++++----- kickstart.py | 6 ++---- partedUtils.py | 6 +----- 4 files changed, 11 insertions(+), 19 deletions(-) diff --git a/installclasses/fedora.py b/installclasses/fedora.py index c183a6eea..bfd9a5556 100644 --- a/installclasses/fedora.py +++ b/installclasses/fedora.py @@ -62,11 +62,10 @@ class InstallClass(BaseInstallClass): def setInstallData(self, anaconda): BaseInstallClass.setInstallData(self, anaconda) - if not anaconda.isKickstart: - BaseInstallClass.setDefaultPartitioning(self, - anaconda.id.storage, - anaconda.platform, - CLEARPART_TYPE_LINUX) + BaseInstallClass.setDefaultPartitioning(self, + anaconda.id.storage, + anaconda.platform, + CLEARPART_TYPE_LINUX) def setSteps(self, anaconda): BaseInstallClass.setSteps(self, anaconda); diff --git a/installclasses/rhel.py b/installclasses/rhel.py index 95bacdf47..521792862 100644 --- a/installclasses/rhel.py +++ b/installclasses/rhel.py @@ -87,11 +87,10 @@ class InstallClass(BaseInstallClass): def setInstallData(self, anaconda): BaseInstallClass.setInstallData(self, anaconda) - if not anaconda.isKickstart: - BaseInstallClass.setDefaultPartitioning(self, - anaconda.id.storage, - anaconda.platform, - CLEARPART_TYPE_LINUX) + BaseInstallClass.setDefaultPartitioning(self, + anaconda.id.storage, + anaconda.platform, + CLEARPART_TYPE_LINUX) def setSteps(self, anaconda): dispatch = anaconda.dispatch diff --git a/kickstart.py b/kickstart.py index f8e5467a5..8a6b743ca 100644 --- a/kickstart.py +++ b/kickstart.py @@ -232,7 +232,7 @@ class ClearPart(commands.clearpart.FC3_ClearPart): if self.type is None: self.type = CLEARPART_TYPE_NONE - hds = isys.hardDriveDict().keys() + hds = map(lambda x: x.name, filter(lambda x: isys.mediaPresent(x.name), self.handler.id.storage.disks)) for disk in self.drives: if disk not in hds: raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in clearpart command" % disk) @@ -600,7 +600,7 @@ class Partition(commands.partition.F9_Partition): raise KickstartValueError, formatErrorMsg(self.lineno, msg="Partition requires a size specification") if pd.start != 0 and pd.disk == "": raise KickstartValueError, formatErrorMsg(self.lineno, msg="Partition command with start cylinder requires a drive specification") - hds = isys.hardDriveDict() + hds = map(lambda x: x.name, filter(lambda x: isys.mediaPresent(x.name), self.handler.id.storage.disks)) if not hds.has_key(pd.disk) and hds.has_key('mapper/'+pd.disk): pd.disk = 'mapper/' + pd.disk if pd.disk != "" and pd.disk not in hds.keys(): @@ -1010,8 +1010,6 @@ def processKickstartFile(anaconda, file): # make sure our disks are alive from partedUtils import DiskSet ds = DiskSet(anaconda) - ds.startMPath() - ds.startDmRaid() # parse the %pre ksparser = KickstartPreParser(AnacondaKSHandler(anaconda)) diff --git a/partedUtils.py b/partedUtils.py index c42dc6748..d3049eb7e 100644 --- a/partedUtils.py +++ b/partedUtils.py @@ -641,14 +641,10 @@ class DiskSet: def driveList (self): """Return the list of drives on the system.""" - drives = isys.hardDriveDict().keys() + drives = map(lambda x: x.name, filter(lambda x: isys.mediaPresent(x.name), self.anaconda.id.storage.disks)) drives.sort (isys.compareDrives) return drives - def drivesByName (self): - """Return a dictionary of the drives on the system.""" - return isys.hardDriveDict() - def savePartitions (self): """Write the partition tables out to the disks.""" for disk in self.disks.values(): -- cgit From 99c80e4eb0c4c4fddfbcd24005db680fa9cb93ff Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 18:00:22 -0500 Subject: If nothing's selected in the bootloader selection combo, don't traceback. This fixes a rarely reported, even more rarely reproduced bug indicating a problem subscripting a TreeIter. --- iw/bootloader_main_gui.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/iw/bootloader_main_gui.py b/iw/bootloader_main_gui.py index fda24c70e..452e880e9 100644 --- a/iw/bootloader_main_gui.py +++ b/iw/bootloader_main_gui.py @@ -80,7 +80,12 @@ class MainBootloaderWindow(InstallWindow): def __driveChange(combo, dxml, choices): if not choices.has_key("mbr"): return - first = combo.get_model()[combo.get_active_iter()][1] + + iter = combo.get_active_iter() + if not iter: + return + + first = combo.get_model()[iter][1] desc = choices["mbr"][1] dxml.get_widget("mbrRadio").set_label("%s - /dev/%s" %(desc, first)) dxml.get_widget("mbrRadio").set_data("bootDevice", first) -- cgit From 78b1415591a1db738d0c09b9de50e501e2048c28 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 4 Mar 2009 18:00:23 -0500 Subject: Fix adding bootloader entries on the bootloader config screen. --- iw/osbootwidget.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/iw/osbootwidget.py b/iw/osbootwidget.py index 24f262695..37a5eba39 100644 --- a/iw/osbootwidget.py +++ b/iw/osbootwidget.py @@ -151,10 +151,12 @@ class OSBootWidget: label = gui.MnemonicLabel(_("_Device")) table.attach(label, 0, 1, 2, 3, gtk.FILL, 0, 10) if not isRoot: + parts = [] + for part in self.storage.partitions: if part.partedPartition.getFlag(parted.PARTITION_LVM) or \ part.partedPartition.getFlag(parted.PARTITION_RAID) or \ - not part.active: + not part.partedPartition.active: continue parts.append(part) -- cgit From aebfe492a2745de8df7ea79c6f6961ffac3caf0a Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 20:51:22 -0600 Subject: Install storage/ with the rest, include it and booty in images. --- Makefile | 3 +++ anaconda | 4 ---- scripts/upd-instroot | 5 +++++ storage/Makefile | 33 +++++++++++++++++++++++++++++++++ storage/devicelibs/Makefile | 33 +++++++++++++++++++++++++++++++++ storage/formats/Makefile | 33 +++++++++++++++++++++++++++++++++ 6 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 storage/Makefile create mode 100644 storage/devicelibs/Makefile create mode 100644 storage/formats/Makefile diff --git a/Makefile b/Makefile index d7186a45b..388af21ff 100644 --- a/Makefile +++ b/Makefile @@ -24,6 +24,7 @@ RELEASE := $(shell awk '/Release:/ { print $$2 }' anaconda.spec) CVSROOT ?= ${CVSROOT:-$(shell cat CVS/Root 2>/dev/null)} SUBDIRS = isys loader po booty \ + storage storage/formats storage/devicelibs \ textw utils scripts bootdisk installclasses \ iw pixmaps command-stubs ui docs # fonts aren't on s390/s390x @@ -90,6 +91,8 @@ install: mkdir -p $(DESTDIR)/$(RUNTIMEDIR) mkdir -p $(DESTDIR)/$(ANACONDADATADIR) + install -m 644 70-anaconda.rules $(DESTDIR)/$(RUNTIMEDIR) + install -m 755 anaconda $(DESTDIR)/usr/sbin/anaconda install -m 755 mini-wm $(DESTDIR)/usr/bin/mini-wm diff --git a/anaconda b/anaconda index 3abce5dcf..0244ad6a1 100755 --- a/anaconda +++ b/anaconda @@ -170,10 +170,6 @@ def setupPythonUpdates(): f), "/tmp/updates/%s/%s" %(pypkg, f)) - import shutil - shutil.copyfile("/tmp/updates/70-anaconda.rules", - "/etc/udev/rules.d/70-anaconda.rules") - def parseOptions(): def resolution_cb (option, opt_str, value, parser): parser.values.runres = value diff --git a/scripts/upd-instroot b/scripts/upd-instroot index 1da86f508..8491bc003 100755 --- a/scripts/upd-instroot +++ b/scripts/upd-instroot @@ -496,6 +496,10 @@ usr/lib/anaconda-runtime usr/lib/anaconda/installclasses usr/lib/anaconda/iw usr/lib/anaconda/textw +usr/lib/anaconda/booty +usr/lib/anaconda/storage +usr/lib/anaconda/storage/devicelibs +usr/lib/anaconda/storage/formats usr/lib/kernel-wrapper usr/lib/locale usr/lib/python?.?/site-packages/bugzilla* @@ -946,6 +950,7 @@ mv $DEST/usr/sbin/anaconda $DEST/usr/bin/anaconda mv $DEST/usr/lib/anaconda-runtime/lib* $DEST/usr/$LIBDIR 2>/dev/null mv $DEST/etc/yum.repos.d $DEST/etc/anaconda.repos.d +cp $DEST/usr/lib/anaconda-runtime/70-anaconda.rules $DEST/lib/udev/rules.d/ rm -f $DEST/usr/$LIBDIR/libunicode-lite* diff --git a/storage/Makefile b/storage/Makefile new file mode 100644 index 000000000..5ef2e0bce --- /dev/null +++ b/storage/Makefile @@ -0,0 +1,33 @@ +# +# Makefile +# +# Copyright (C) 2007 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +include ../Makefile.inc + +all: + echo "nothing to make" + +install: + mkdir -p $(DESTDIR)/$(PYTHONLIBDIR)/storage + install -p -m 644 *.py $(DESTDIR)/$(PYTHONLIBDIR)/storage + ../py-compile --basedir $(DESTDIR)/$(PYTHONLIBDIR)/storage $(DESTDIR)/$(PYTHONLIBDIR)/storage/*.py + +clean: + rm -f *.o *.so *.pyc + +depend: diff --git a/storage/devicelibs/Makefile b/storage/devicelibs/Makefile new file mode 100644 index 000000000..2ecda8c8a --- /dev/null +++ b/storage/devicelibs/Makefile @@ -0,0 +1,33 @@ +# +# Makefile +# +# Copyright (C) 2007 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +include ../../Makefile.inc + +all: + echo "nothing to make" + +install: + mkdir -p $(DESTDIR)/$(PYTHONLIBDIR)/storage/devicelibs + install -p -m 644 *.py $(DESTDIR)/$(PYTHONLIBDIR)/storage/devicelibs + ../../py-compile --basedir $(DESTDIR)/$(PYTHONLIBDIR)/storage/devicelibs $(DESTDIR)/$(PYTHONLIBDIR)/storage/devicelibs/*.py + +clean: + rm -f *.o *.so *.pyc + +depend: diff --git a/storage/formats/Makefile b/storage/formats/Makefile new file mode 100644 index 000000000..c99a94929 --- /dev/null +++ b/storage/formats/Makefile @@ -0,0 +1,33 @@ +# +# Makefile +# +# Copyright (C) 2007 Red Hat, Inc. All rights reserved. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# + +include ../../Makefile.inc + +all: + echo "nothing to make" + +install: + mkdir -p $(DESTDIR)/$(PYTHONLIBDIR)/storage/formats + install -p -m 644 *.py $(DESTDIR)/$(PYTHONLIBDIR)/storage/formats + ../../py-compile --basedir $(DESTDIR)/$(PYTHONLIBDIR)/storage/formats $(DESTDIR)/$(PYTHONLIBDIR)/storage/formats/*.py + +clean: + rm -f *.o *.so *.pyc + +depend: -- cgit From 42b55f58f2f8d9eb8be00de25e94cd73ad3f32eb Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 21:21:54 -0600 Subject: Add central methods to write out storage configuration. --- storage/__init__.py | 49 +++++++++++++++++++++++++++++++++++++------------ yuminstall.py | 3 +-- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 11e2e7b51..b94af3aff 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -659,6 +659,16 @@ class Storage(object): return True return False + def write(self, instPath): + self.fsset.write(instPath) + self.iscsi.write(instPath, self.anaconda) + self.zfcp.write(instPath) + + def writeKS(self, f): + log.warning("Storage.writeKS not completely implemented") + self.iscsi.writeKS(f) + self.zfcp.writeFS(f) + def getReleaseString(mountpoint): relName = None @@ -1335,9 +1345,22 @@ class FSSet(object): return migratable - def write(self, chroot="/"): + def write(self, instPath): """ write out all config files based on the set of filesystems """ - pass + # /etc/fstab + fstab_path = os.normpath("%s/etc/fstab" % instPath) + fstab = self.fstab() + open(fstab_path, "w").write(fstab) + + # /etc/crypttab + crypttab_path = os.normpath("%s/etc/crypttab" % instPath) + crypttab = self.crypttab() + open(crypttab_path, "w").write(crypttab) + + # /etc/mdadm.conf + mdadm_path = os.normpath("%s/etc/mdadm.conf" % instPath) + mdadm_conf = self.mdadmConf() + open(mdadm_path, "w").write(mdadm_conf) def crypttab(self): # if we are upgrading, do we want to update crypttab? @@ -1347,13 +1370,15 @@ class FSSet(object): self.cryptTab = CryptTab(self.devicetree) self.cryptTab.populate() + devices = self.mountpoints.values() + self.swapDevices + # prune crypttab -- only mappings required by one or more entries for name in self.cryptTab.mappings: keep = False mapInfo = self.cryptTab[name] cryptoDev = mapInfo['device'] - for device in self.devices: - if device.dependsOn(cryptoDev): + for device in devices: + if device == cryptoDev or device.dependsOn(cryptoDev): keep = True break @@ -1366,15 +1391,13 @@ class FSSet(object): """ Return the contents of mdadm.conf. """ arrays = self.devicetree.getDevicesByType("mdarray") conf = "" + devices = self.mountpoints.values() + self.swapDevices for array in arrays: writeConf = False - if array in self.devices: - writeConf = True - else: - for device in self.devices: - if device.dependsOn(array): - writeConf = True - break + for device in devices: + if device == array or devices.dependsOn(array): + writeConf = True + break if writeConf: conf += array.mdadmConfEntry @@ -1397,7 +1420,9 @@ class FSSet(object): # """ % time.asctime() - for device in self.devices: + + devices = self.mountpoints.values() + self.swapDevices + for device in devices: # why the hell do we put swap in the fstab, anyway? if not device.format.mountable and device.format.type != "swap": continue diff --git a/yuminstall.py b/yuminstall.py index f30d7aa67..4135815b8 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -1497,8 +1497,7 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon shutil.copyfile("/etc/modprobe.d/anaconda", anaconda.rootPath + "/etc/modprobe.d/anaconda") anaconda.id.network.write(instPath=anaconda.rootPath, anaconda=anaconda) - anaconda.id.storage.iscsi.write(anaconda.rootPath, anaconda) - anaconda.id.storage.zfcp.write(anaconda.rootPath) + anaconda.id.storage.write(anaconda.rootPath) if not anaconda.id.isHeadless: anaconda.id.keyboard.write(anaconda.rootPath) -- cgit From f1aea31823eb662b18b89b1edb6ac34058342e72 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 21:37:45 -0600 Subject: Bump version to 11.5.0.24 for storage test day build. --- anaconda.spec | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index 2657076e6..18123f89f 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.23 +Version: 11.5.0.24 Release: 1 License: GPLv2+ Group: Applications/System @@ -209,6 +209,9 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Wed Mar 4 2009 Dave Lehman - 11.5.0.24-1 +- Storage test day. + * Fri Feb 20 2009 David Cantrell - 11.5.0.23-1 - Remove old content from utils/ (dcantrell) - Ensure request.drive is always a list (#485622) (dcantrell) -- cgit From 4fb446e75fbc7e10621896ea10660cbb2ca8cfda Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 4 Mar 2009 23:49:08 -0600 Subject: Implement Storage.sanityCheck, mostly from old partitions code. Also catch exceptions thrown by platform.bootDevice() and add mdRaidBootArches from old raid.py to storage.devicelibs.mdraid for use by the sanityCheck code. --- storage/__init__.py | 118 +++++++++++++++++++++++++++++++++++++++++-- storage/devicelibs/mdraid.py | 2 + storage/partitioning.py | 6 ++- 3 files changed, 122 insertions(+), 4 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index b94af3aff..524d3c24b 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -40,6 +40,7 @@ from formats import getFormat from formats import get_device_format_class from formats import get_default_filesystem_type from devicelibs.lvm import safeLvmName +from devicelibs.mdraid import mdRaidBootArches from udev import udev_trigger import iscsi import zfcp @@ -640,9 +641,120 @@ class Storage(object): return lvtemplate def sanityCheck(self): - """ Run a series of tests to verify the storage configuration. """ - log.warning("storage.Storage.sanityCheck is unimplemented") - return ([], []) + """ Run a series of tests to verify the storage configuration. + + This function is called at the end of partitioning so that + we can make sure you don't have anything silly (like no /, + a really small /, etc). Returns (errors, warnings) where + each is a list of strings. + """ + checkSizes = [('/usr', 250), ('/tmp', 50), ('/var', 384), + ('/home', 100), ('/boot', 75)] + warnings = [] + errors = [] + + filesystems = self.fsset.mountpoints + root = self.fsset.rootDevice + swaps = self.fsset.swapDevices + try: + boot = self.anaconda.platform.bootDevice() + except DeviceError: + boot = None + + if not root: + errors.append(_("You have not defined a root partition (/), " + "which is required for installation of %s " + "to continue.") % (productName,)) + + if root and root.size < 250: + warnings.append(_("Your root partition is less than 250 " + "megabytes which is usually too small to " + "install %s.") % (productName,)) + + if (root and + root.size < self.anaconda.backend.getMinimumSizeMB("/")): + errors.append(_("Your / partition is less than %s " + "megabytes which is lower than recommended " + "for a normal %s install.") + %(self.anaconda.backend.getMinimumSizeMB("/"), + productName)) + + for (mount, size) in checkSizes: + if mount in filesystems and filesystems[mount].size < size: + warnings.append(_("Your %s partition is less than %s " + "megabytes which is lower than recommended " + "for a normal %s install.") + %(mount, size, productName)) + + usb_disks = [] + firewire_disks = [] + for disk in self.disks: + if isys.driveUsesModule(disk.name, ["usb-storage", "ub"]): + usb_disks.append(disk) + elif isys.driveUsesModule(disk.name, ["sbp2", "firewire-sbp2"]): + firewire_disks.append(disk) + + uses_usb = False + uses_firewire = False + for device in filesystems.values() + for disk in usb_disks: + if device.dependsOn(disk): + uses_usb = True + break + + for disk in firewire_disks: + if device.dependsOn(disk): + uses_firewire = True + break + + if uses_usb: + warnings.append(_("Installing on a USB device. This may " + "or may not produce a working system.")) + if uses_firewire: + warnings.append(_("Installing on a FireWire device. This may " + "or may not produce a working system.")) + + if not boot: + errors.append(_("You have not created a boot partition.")) + + if (boot and boot.type == "mdarray" and + boot.level != 1): + errors.append(_("Bootable partitions can only be on RAID1 " + "devices.")) + + # can't have bootable partition on LV + if boot and boot.type == "lvmlv": + errors.append(_("Bootable partitions cannot be on a " + "logical volume.")) + + # most arches can't have boot on RAID + if (boot and boot.type == "mdarray" and + iutil.getArch() not in mdRaidBootArches): + errors.append(_("Bootable partitions cannot be on a RAID " + "device.")) + + # Lots of filesystems types don't support /boot. + if boot and not boot.format.bootable: + errors.append(_("Bootable partitions cannot be on an %s " + "filesystem.") % boot.format.name) + + # vfat /boot is insane. + if (boot and boot == rootDevice and boot.format.type == "vfat"): + errors.append(_("Bootable partitions cannot be on an %s " + "filesystem.") % boot.format.type) + + if (boot and filter(lambda d: d.type == "luks/dm-crypt", + self.deviceDeps(boot))): + errors.append(_("Bootable partitions cannot be on an " + "encrypted block device")) + + if not swapDevices: + warnings.append(_("You have not specified a swap partition. " + "Although not strictly required in all cases, " + "it will significantly improve performance for " + "most installations.")) + + return (errors, warnings) def isProtected(self, device): """ Return True is the device is protected. """ diff --git a/storage/devicelibs/mdraid.py b/storage/devicelibs/mdraid.py index a0d2f6ae3..12f429f93 100644 --- a/storage/devicelibs/mdraid.py +++ b/storage/devicelibs/mdraid.py @@ -28,6 +28,8 @@ from ..errors import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) +mdRaidBootArches = ("i386", "x86_64", "ppc") + def getRaidLevels(): avail = [] try: diff --git a/storage/partitioning.py b/storage/partitioning.py index 6bb0333c8..a69751a37 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -502,7 +502,11 @@ def doPartitioning(storage, exclusiveDisks=None): partitions = storage.partitions # FIXME: isn't there a better place for this to happen? - bootDev = anaconda.platform.bootDevice() + try: + bootDev = anaconda.platform.bootDevice() + except DeviceError: + bootDev = None + if bootDev and not bootDev.exists: bootDev.req_bootable = True -- cgit From 4360a5a38d908fc63df5d3a813cdebf4294df50c Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Thu, 5 Mar 2009 11:04:03 +0100 Subject: Fix syntax errors This patches against anaconda-storage-branch get me to package installation (with Custom non-lvm partitioning). They fix 4 tracebacks i met on the way there. (applied to 4fb446e75fbc7e10621896ea10660cbb2ca8cfda) --- storage/__init__.py | 6 +++--- storage/devices.py | 1 - 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 524d3c24b..947b0d4df 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -696,7 +696,7 @@ class Storage(object): uses_usb = False uses_firewire = False - for device in filesystems.values() + for device in filesystems.values(): for disk in usb_disks: if device.dependsOn(disk): uses_usb = True @@ -739,7 +739,7 @@ class Storage(object): "filesystem.") % boot.format.name) # vfat /boot is insane. - if (boot and boot == rootDevice and boot.format.type == "vfat"): + if (boot and boot == root and boot.format.type == "vfat"): errors.append(_("Bootable partitions cannot be on an %s " "filesystem.") % boot.format.type) @@ -748,7 +748,7 @@ class Storage(object): errors.append(_("Bootable partitions cannot be on an " "encrypted block device")) - if not swapDevices: + if not swaps: warnings.append(_("You have not specified a swap partition. " "Although not strictly required in all cases, " "it will significantly improve performance for " diff --git a/storage/devices.py b/storage/devices.py index 27d9a1f1e..14fbb1144 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -594,7 +594,6 @@ class DiskDevice(StorageDevice): self.partedDevice = None self.partedDisk = None - self.removable = False log.debug("looking up parted Device: %s" % self.path) self.partedDevice = parted.Device(path=self.path) if not self.partedDevice: -- cgit From 5997cccc7dc69e6472828be6c403c0a8e71fba09 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Thu, 5 Mar 2009 12:58:39 +0100 Subject: More syntax errors / traceback fixes More syntax errors / traceback fixes. --- installclasses/fedora.py | 3 +++ storage/__init__.py | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/installclasses/fedora.py b/installclasses/fedora.py index bfd9a5556..087e2d377 100644 --- a/installclasses/fedora.py +++ b/installclasses/fedora.py @@ -79,6 +79,9 @@ class InstallClass(BaseInstallClass): return yuminstall.YumBackend def productMatches(self, oldprod): + if oldprod is None: + return False + if oldprod.startswith(productName): return True diff --git a/storage/__init__.py b/storage/__init__.py index 947b0d4df..0ff37ace8 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -951,7 +951,7 @@ class BlkidTab(object): class CryptTab(object): """ Dictionary-like interface to crypttab entries with map name keys """ - def __init__(self, devicetree, blkidTab=None): + def __init__(self, devicetree, blkidTab=None, chroot=""): self.devicetree = devicetree self.blkidTab = blkidTab self.chroot = chroot @@ -1105,7 +1105,7 @@ class FSSet(object): log.info("error parsing blkid.tab: %s" % e) blkidTab = None - cryptTab = CryptTab(self.devicetree, blkidTab=blkidTab) + cryptTab = CryptTab(self.devicetree, blkidTab=blkidTab, chroot=chroot) try: cryptTab.parse(chroot=chroot) log.debug("crypttab maps: %s" % cryptTab.mappings.keys()) @@ -1460,17 +1460,17 @@ class FSSet(object): def write(self, instPath): """ write out all config files based on the set of filesystems """ # /etc/fstab - fstab_path = os.normpath("%s/etc/fstab" % instPath) + fstab_path = os.path.normpath("%s/etc/fstab" % instPath) fstab = self.fstab() open(fstab_path, "w").write(fstab) # /etc/crypttab - crypttab_path = os.normpath("%s/etc/crypttab" % instPath) + crypttab_path = os.path.normpath("%s/etc/crypttab" % instPath) crypttab = self.crypttab() open(crypttab_path, "w").write(crypttab) # /etc/mdadm.conf - mdadm_path = os.normpath("%s/etc/mdadm.conf" % instPath) + mdadm_path = os.path.normpath("%s/etc/mdadm.conf" % instPath) mdadm_conf = self.mdadmConf() open(mdadm_path, "w").write(mdadm_conf) -- cgit From 0dcd327c5ff8154be31f640968972c272b42b51b Mon Sep 17 00:00:00 2001 From: Martin Sivak Date: Thu, 5 Mar 2009 12:40:31 +0100 Subject: If a drive is not initialized, offer reinitialization or ignoring the drive to the user --- storage/devices.py | 11 +++++++++-- storage/devicetree.py | 28 ++++++++++++++++++++++++++-- 2 files changed, 35 insertions(+), 4 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 14fbb1144..c97ce7f96 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -569,7 +569,7 @@ class DiskDevice(StorageDevice): def __init__(self, device, format=None, size=None, major=None, minor=None, - sysfsPath='', parents=None): + sysfsPath='', parents=None, init = False, labeltype = None): """ Create a DiskDevice instance. Arguments: @@ -586,6 +586,10 @@ class DiskDevice(StorageDevice): parents -- a list of required Device instances removable -- whether or not this is a removable device + init -- initialize label on this drive + labeltype -- type of the label to use during initialization + + DiskDevices always exist. """ StorageDevice.__init__(self, device, format=format, size=size, @@ -599,7 +603,10 @@ class DiskDevice(StorageDevice): if not self.partedDevice: raise DeviceError("cannot find parted device instance") log.debug("creating parted Disk: %s" % self.path) - self.partedDisk = parted.Disk(device=self.partedDevice) + if init: + self.partedDisk = parted.freshDisk(device=self.partedDevice, ty = labeltype) + else: + self.partedDisk = parted.Disk(device=self.partedDevice) if not self.partedDisk: raise DeviceError("failed to create parted Disk") diff --git a/storage/devicetree.py b/storage/devicetree.py index 7f511347c..4ef2e4d97 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -27,6 +27,8 @@ from devices import * from deviceaction import * import formats from udev import * +import parted +import platform import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -743,11 +745,33 @@ class DeviceTree(object): log.debug("%s is a disk" % name) device = self.getDeviceByName(name) if device is None: - device = DiskDevice(name, + try: + device = DiskDevice(name, major=udev_device_get_major(info), minor=udev_device_get_minor(info), sysfsPath=sysfs_path) - self._addDevice(device) + self._addDevice(device) + except parted.IOException: #drive not initialized? + if not self.intf: + self.ignoredDisks.append(name) + else: + rc = self.intf.messageWindow(_("Warning"), + _("Error processing drive %s.\n" + "Maybe it needs to be reinitialized." + "YOU WILL LOSE ALL DATA ON THIS DRIVE!" % (name,)), + type="custom", + custom_buttons = [ _("_Ignore drive"), + _("_Re-initialize drive") ], + custom_icon="question") + if rc == 0: + self.ignoredDisks.append(name) + else: + device = DiskDevice(name, + major=udev_device_get_major(info), + minor=udev_device_get_minor(info), + sysfsPath=sysfs_path, init = True, + labeltype = platform.getPlatform(None).diskType) + self._addDevice(device) elif udev_device_is_partition(info): log.debug("%s is a partition" % name) device = self.getDeviceByName(name) -- cgit From d7dd91babf2a8c9666765da5ed4ca1b53f27e3aa Mon Sep 17 00:00:00 2001 From: Martin Sivak Date: Thu, 5 Mar 2009 13:56:41 +0100 Subject: We need newer python-cryptsetup because of the default values for cipher and keysize for luskFormat --- anaconda.spec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index 18123f89f..5dc8a03c0 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -41,7 +41,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) %define createrepover 0.4.7 %define yumutilsver 1.1.11-3 %define iscsiver 6.2.0.870-3 -%define pythoncryptsetupver 0.0.6 +%define pythoncryptsetupver 0.0.7 BuildRequires: audit-libs-devel BuildRequires: bzip2-devel -- cgit From 100611e0201cc19668ca8f642dd5bc437d2749ba Mon Sep 17 00:00:00 2001 From: Martin Sivak Date: Thu, 5 Mar 2009 13:58:34 +0100 Subject: Format message string after translation not before --- storage/devicetree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index 4ef2e4d97..c165eebcc 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -758,7 +758,7 @@ class DeviceTree(object): rc = self.intf.messageWindow(_("Warning"), _("Error processing drive %s.\n" "Maybe it needs to be reinitialized." - "YOU WILL LOSE ALL DATA ON THIS DRIVE!" % (name,)), + "YOU WILL LOSE ALL DATA ON THIS DRIVE!") % (name,), type="custom", custom_buttons = [ _("_Ignore drive"), _("_Re-initialize drive") ], -- cgit From bb6b3a050d0a007cea27bdd716280c19bb104071 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 08:56:02 -0600 Subject: Stop with the fsset usage. --- booty/alpha.py | 4 ++-- booty/bootloaderInfo.py | 2 +- booty/checkbootloader.py | 2 +- booty/ppc.py | 2 +- booty/util.py | 33 +++++++++++++++++++++++++++++++++ booty/x86.py | 14 +++++++------- 6 files changed, 45 insertions(+), 12 deletions(-) create mode 100644 booty/util.py diff --git a/booty/alpha.py b/booty/alpha.py index b5ed98a47..abd3a636a 100644 --- a/booty/alpha.py +++ b/booty/alpha.py @@ -3,11 +3,11 @@ import iutil from booty import BootyNoKernelWarning from bootloaderInfo import * -import fsset +from util import getDiskPart class alphaBootloaderInfo(bootloaderInfo): def wholeDevice (self, path): - (device, foo) = fsset.getDiskPart(path) + (device, foo) = getDiskPart(path) return device def partitionNum (self, path): diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 4d5c343cf..f9f684b95 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -29,13 +29,13 @@ import rhpl from rhpl.translate import _, N_ from flags import flags -from fsset import getDiskPart import iutil import isys from product import * import booty import checkbootloader +from util import getDiskPart if rhpl.getArch() not in ("s390", "s390x"): import block diff --git a/booty/checkbootloader.py b/booty/checkbootloader.py index 68cb8e13f..7c6241676 100644 --- a/booty/checkbootloader.py +++ b/booty/checkbootloader.py @@ -19,7 +19,7 @@ import os import string import rhpl -from fsset import getDiskPart +from util import getDiskPart import iutil grubConfigFile = "/etc/grub.conf" diff --git a/booty/ppc.py b/booty/ppc.py index d653052ce..a68c5938d 100644 --- a/booty/ppc.py +++ b/booty/ppc.py @@ -2,8 +2,8 @@ import string import os from booty import BootyNoKernelWarning +from util import getDiskPart from bootloaderInfo import * -import fsset import iutil import rhpl diff --git a/booty/util.py b/booty/util.py new file mode 100644 index 000000000..f9a1b3e84 --- /dev/null +++ b/booty/util.py @@ -0,0 +1,33 @@ +import string + +def getDiskPart(dev): + cut = len(dev) + if (dev.startswith('rd/') or dev.startswith('ida/') or + dev.startswith('cciss/') or dev.startswith('sx8/') or + dev.startswith('mapper/') or dev.startswith('mmcblk')): + if dev[-2] == 'p': + cut = -1 + elif dev[-3] == 'p': + cut = -2 + else: + if dev[-2] in string.digits: + cut = -2 + elif dev[-1] in string.digits: + cut = -1 + + name = dev[:cut] + + # hack off the trailing 'p' from /dev/cciss/*, for example + if name[-1] == 'p': + for letter in name: + if letter not in string.letters and letter != "/": + name = name[:-1] + break + + if cut < 0: + partNum = int(dev[cut:]) - 1 + else: + partNum = None + + return (name, partNum) + diff --git a/booty/x86.py b/booty/x86.py index 2e01935d3..de88458e8 100644 --- a/booty/x86.py +++ b/booty/x86.py @@ -2,9 +2,9 @@ import os import string from booty import BootyNoKernelWarning +from util import getDiskPart from bootloaderInfo import * import checkbootloader -import fsset import iutil import rhpl @@ -107,7 +107,7 @@ class x86BootloaderInfo(efiBootloaderInfo): cmds = [] for bootDev in bootDevs: gtPart = self.getMatchingPart(bootDev, grubTarget) - gtDisk = self.grubbyPartitionName(fsset.getDiskPart(gtPart)[0]) + gtDisk = self.grubbyPartitionName(getDiskPart(gtPart)[0]) bPart = self.grubbyPartitionName(bootDev) cmd = "root %s\n" % (bPart,) @@ -316,7 +316,7 @@ class x86BootloaderInfo(efiBootloaderInfo): devs = usedDevs.keys() usedDevs = {} for dev in devs: - drive = fsset.getDiskPart(dev)[0] + drive = getDiskPart(dev)[0] if usedDevs.has_key(drive): continue usedDevs[drive] = 1 @@ -356,10 +356,10 @@ class x86BootloaderInfo(efiBootloaderInfo): return "" def getMatchingPart(self, bootDev, target): - bootName, bootPartNum = fsset.getDiskPart(bootDev) + bootName, bootPartNum = getDiskPart(bootDev) devices = self.getPhysicalDevices(target) for device in devices: - name, partNum = fsset.getDiskPart(device) + name, partNum = getDiskPart(device) if name == bootName: return device return devices[0] @@ -368,7 +368,7 @@ class x86BootloaderInfo(efiBootloaderInfo): return "hd%d" % self.drivelist.index(name) def grubbyPartitionName(self, dev): - (name, partNum) = fsset.getDiskPart(dev) + (name, partNum) = getDiskPart(dev) if partNum != None: return "(%s,%d)" % (self.grubbyDiskName(name), partNum) else: @@ -478,7 +478,7 @@ class x86BootloaderInfo(efiBootloaderInfo): grubbyRootPart = self.grubbyPartitionName(rootDevs[0]) for rootDev in rootDevs: - testGrubbyRootDev = fsset.getDiskPart(rootDev)[0] + testGrubbyRootDev = getDiskPart(rootDev)[0] testGrubbyRootDev = self.grubbyPartitionName(testGrubbyRootDev) if grubbyStage1Dev == testGrubbyRootDev: -- cgit From 9f30febc2df2ebfc64e54470a3d7a9cbdbb3d8d6 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 5 Mar 2009 09:40:34 -0500 Subject: Add a get() method to Flags, since it pretends to be a dictionary. --- flags.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/flags.py b/flags.py index 2d77bd794..8dbcc9f9d 100644 --- a/flags.py +++ b/flags.py @@ -36,6 +36,12 @@ class Flags: else: raise AttributeError, attr + def get(self, attr, val=None): + if self.__dict__['flags'].has_key(attr): + return self.__dict__['flags'][attr] + else: + return val + def createCmdlineDict(self): cmdlineDict = {} cmdline = open("/proc/cmdline", "r").read().strip() -- cgit From b248e53ddf05a660ff0f98ab22d039cdd7c9f025 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 09:31:15 -0600 Subject: Iterate over devicetree.devices.values, not devicetree. --- storage/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/__init__.py b/storage/__init__.py index 0ff37ace8..1b42fbd08 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -996,7 +996,7 @@ class CryptTab(object): def populate(self): """ Populate the instance based on the device tree's contents. """ - for device in self.devicetree: + for device in self.devicetree.devices.values(): # XXX should we put them all in there or just the ones that # are part of a device containing swap or a filesystem? # -- cgit From b56e54b86942a293a7c264c46510a7db522769da Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 5 Mar 2009 10:40:56 -0500 Subject: When building the exceptionDisks list, skip devices libparted doesn't like. libparted does not probe for a lot of devices, notably things like /dev/sr0. There's no real way to handle this in pyparted since we can't distinguish a valid but unprobeable device from an invalid device at that layer. So, anaconda needs to skip anything libparted refuses to probe. --- storage/__init__.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/storage/__init__.py b/storage/__init__.py index 1b42fbd08..1ac6c0d1b 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -383,7 +383,11 @@ class Storage(object): if not device.removable: continue - dev = parted.Device(path=device.path) + try: + dev = parted.Device(path=device.path) + except parted.DeviceException: + continue + disk = parted.Disk(device=dev) for part in disk.partitions: if part.active and \ -- cgit From 755478fcb2fd9ee5d65f907199ade7359f90f6a1 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 5 Mar 2009 11:10:59 -0500 Subject: partRequests no longer exists, so don't try to import it (#488743). --- packages.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages.py b/packages.py index 2bac9d331..2dd562d6a 100644 --- a/packages.py +++ b/packages.py @@ -240,8 +240,6 @@ def setupTimezone(anaconda): # FIXME: this is a huge gross hack. hard coded list of files # created by anaconda so that we can not be killed by selinux def setFileCons(anaconda): - import partRequests - if flags.selinux: log.info("setting SELinux contexts for anaconda created files") -- cgit From 655419771822f9bb32f227957c6a3f1bed036ce2 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 10:39:54 -0600 Subject: Handle non-fatal errors more gracefully in addUdevDevice. --- storage/devicetree.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index c165eebcc..3494d5a1d 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -819,7 +819,10 @@ class DeviceTree(object): kwargs["mapName"] = "luks-%s" % uuid elif format_type == "linux_raid_member": # mdraid - kwargs["mdUuid"] = udev_device_get_md_uuid(info) + try: + kwargs["mdUuid"] = udev_device_get_md_uuid(info) + except KeyError: + log.debug("mdraid member %s has no md uuid" % name) elif format_type == "isw_raid_member": # dmraid # TODO: collect name of containing raidset @@ -831,8 +834,14 @@ class DeviceTree(object): kwargs["vgName"] = udev_device_get_vg_name(info) except KeyError as e: log.debug("PV %s has no vg_name" % name) - kwargs["vgUuid"] = udev_device_get_vg_uuid(info) - kwargs["peStart"] = udev_device_get_pv_pe_start(info) + try: + kwargs["vgUuid"] = udev_device_get_vg_uuid(info) + except KeyError: + log.debug("PV %s has no vg_uuid" % name) + try: + kwargs["peStart"] = udev_device_get_pv_pe_start(info) + except KeyError: + log.debug("PV %s has no pe_start" % name) format = formats.getFormat(*args, **kwargs) device.format = format -- cgit From 1fed7809dc4704c78a54b0f33673a1107e374b51 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 5 Mar 2009 11:41:45 -0500 Subject: Check to see if we're on S390 on the congrats screen (#488747). This continues the war on calling iutil.isWhatever() everywhere. We can start using the platform module for that. --- iw/congrats_gui.py | 3 ++- textw/complete_text.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/iw/congrats_gui.py b/iw/congrats_gui.py index b5f0101cf..0df2d807e 100644 --- a/iw/congrats_gui.py +++ b/iw/congrats_gui.py @@ -23,6 +23,7 @@ import gui from iw_gui import * from constants import * import os +import platform import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -69,7 +70,7 @@ class CongratulationWindow (InstallWindow): a.set_size_request(200, -1) hbox.pack_start (a, False, False, 36) - if iutil.isS390(): + if isinstance(anaconda.platform, platform.S390): txt = _("Congratulations, your %s installation is complete.\n\n") % (productName,) if not anaconda.canReIPL: diff --git a/textw/complete_text.py b/textw/complete_text.py index 1793cab10..fc4e5c1f9 100644 --- a/textw/complete_text.py +++ b/textw/complete_text.py @@ -22,6 +22,7 @@ from snack import * from constants_text import * from constants import * import gettext +import platform _ = lambda x: gettext.ldgettext("anaconda", x) class FinishedWindow: @@ -34,7 +35,7 @@ class FinishedWindow: screen.pushHelpLine (string.center(bottomstr, screen.width)) - if iutil.isS390(): + if isinstance(anaconda.platform, platform.S390): txt = _("Congratulations, your %s installation is complete.\n\n") % (productName,) if not anaconda.canReIPL: -- cgit From 912eed846c221cda7787a259d9ea398122f7e826 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 5 Mar 2009 13:49:13 -0500 Subject: Remove all uses of isys.cdromList, which no longer exists. --- anaconda | 10 ++++++---- image.py | 14 +++++++------- iw/task_gui.py | 4 ++-- yuminstall.py | 2 +- 4 files changed, 16 insertions(+), 14 deletions(-) diff --git a/anaconda b/anaconda index 0244ad6a1..fc32dfb65 100755 --- a/anaconda +++ b/anaconda @@ -1016,10 +1016,12 @@ if __name__ == "__main__": handleException(anaconda, sys.exc_info()) if anaconda.isKickstart and anaconda.id.ksdata.reboot.eject: - isys.flushDriveDict() - for drive in isys.cdromList(): - log.info("attempting to eject %s" % drive) - isys.ejectCdrom(drive) + for drive in anaconda.id.storage.devicetree.devices.values(): + if drive.type != "cdrom": + continue + + log.info("attempting to eject %s" % drive.path) + isys.ejectCdrom(drive.path) del anaconda.intf diff --git a/image.py b/image.py index fc687fd38..83e74dbb2 100644 --- a/image.py +++ b/image.py @@ -249,12 +249,13 @@ def presentRequiredMediaMessage(anaconda): # Find an attached CD/DVD drive with media in it that contains packages, # and return that device name. -def scanForMedia(tree): - drive = None +def scanForMedia(tree, storage): + for dev in storage.devicetree.devices.values(): + if dev.type != "cdrom": + continue - for cdr in map(lambda d: "/dev/%s" % d, isys.cdromList()): try: - if isys.mount(cdr, tree, fstype="iso9660", readOnly=1): + if isys.mount(dev.path, tree, fstype="iso9660", readOnly=1): continue except: continue @@ -263,10 +264,9 @@ def scanForMedia(tree): isys.umount(tree) continue - drive = cdr - break + return dev.path - return drive + return None def umountImage(tree, currentMedia): if currentMedia is not None: diff --git a/iw/task_gui.py b/iw/task_gui.py index 04032d3ea..5f688f5c5 100644 --- a/iw/task_gui.py +++ b/iw/task_gui.py @@ -268,7 +268,7 @@ class RepoEditor: return True def _applyMedia(self, repo): - cdr = scanForMedia(self.anaconda.backend.ayum.tree) + cdr = scanForMedia(self.anaconda.backend.ayum.tree, self.anaconda.id.storage) if not cdr: self.intf.messageWindow(_("No Media Found"), _("No installation media was found. " @@ -393,7 +393,7 @@ class RepoMethodstrEditor(RepoEditor): return repourl def _applyMedia(self): - cdr = scanForMedia(self.anaconda.backend.ayum.tree) + cdr = scanForMedia(self.anaconda.backend.ayum.tree, self.anaconda.id.storage) if not cdr: self.intf.messageWindow(_("No Media Found"), _("No installation media was found. " diff --git a/yuminstall.py b/yuminstall.py index 4135815b8..9ce4883ee 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -444,7 +444,7 @@ class AnacondaYum(YumSorter): # we should first check to see if there's a CD/DVD with packages # on it, and then default to the mirrorlist URL. The user can # always change the repo with the repo editor later. - cdr = scanForMedia(self.tree) + cdr = scanForMedia(self.tree, self.anaconda.id.storage) if cdr: self.mediagrabber = self.mediaHandler self.anaconda.mediaDevice = cdr -- cgit From 0727d7c3665c748f94b8aa50be2e485ca0e1dd96 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 5 Mar 2009 14:40:06 -0500 Subject: Fix a typo when writing out the mdadm config file. --- storage/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/__init__.py b/storage/__init__.py index 1ac6c0d1b..6098759f2 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1511,7 +1511,7 @@ class FSSet(object): for array in arrays: writeConf = False for device in devices: - if device == array or devices.dependsOn(array): + if device == array or device.dependsOn(array): writeConf = True break -- cgit From 4016d43f98cb3be04478a051233172a9ea706161 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 5 Mar 2009 14:47:02 -0500 Subject: Fix a couple problems on the "Change device" bootloader dialog. (1) Add the active boot partition to the drop down BIOS drive list. (2) If there's no active device (not sure how this could happen, but we've gotten bugs about it before) then don't try to do anything else with that combo box. --- iw/bootloader_main_gui.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/iw/bootloader_main_gui.py b/iw/bootloader_main_gui.py index 452e880e9..a11e3b964 100644 --- a/iw/bootloader_main_gui.py +++ b/iw/bootloader_main_gui.py @@ -104,7 +104,7 @@ class MainBootloaderWindow(InstallWindow): i = model.append(None) model[i] = ("%s %8.0f MB %s" %(disk.name, size, m), "%s" %(disk.name,)) - if disk == active: + if disk.name == active: combo.set_active_iter(i) return model @@ -128,8 +128,7 @@ class MainBootloaderWindow(InstallWindow): else: w.set_active(False) w.set_data("bootDevice", device) - - + for i in range(1, 5): if len(self.driveorder) < i: break @@ -160,8 +159,13 @@ class MainBootloaderWindow(InstallWindow): for i in range(1, 5): if len(self.driveorder) < i: break + combo = dxml.get_widget("bd%dCombo" %(i,)) - act = combo.get_model()[combo.get_active_iter()][1] + iter = combo.get_active_iter() + if not iter: + continue + + act = combo.get_model()[iter][1] if act not in neworder: neworder.append(act) for d in self.driveorder: -- cgit From 83fef98ba14c5c1f6a138f47cf964ea24033d877 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 5 Mar 2009 15:07:12 -0500 Subject: Do an even more thorough job of ignoring disks libparted doesn't like. Sometimes, libparted CAN probe /dev/sr0. In those cases, we fail to make a parted.Disk for it becuase there's obviously no disk label on a CD-ROM. For now we'll just handle all possible tracebacks. Later, we will want to do a better job of filtering out exceptions and possibly prompt for initializing disks. --- storage/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 6098759f2..c59b3bbee 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -385,10 +385,10 @@ class Storage(object): try: dev = parted.Device(path=device.path) - except parted.DeviceException: + disk = parted.Disk(device=dev) + except: continue - disk = parted.Disk(device=dev) for part in disk.partitions: if part.active and \ not part.getFlag(parted.PARTITION_RAID) and \ -- cgit From f71453e654d5c2212cc2fa3e6f215d3684937c0d Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Wed, 4 Mar 2009 09:45:02 -1000 Subject: Do not raise DeviceError if not bootable device is found. Continuation of 8fe8f5478e84adbe28cca63d72b9ea6552ae79e9 for the other platforms listed in platform.py. --- platform.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/platform.py b/platform.py index 2c35a5cbf..6db026e3e 100644 --- a/platform.py +++ b/platform.py @@ -126,12 +126,7 @@ class EFI(Platform): def bootDevice(self): mntDict = self._mntDict() - bootDev = mntDict.get("/boot/efi") - - if not bootDev: - raise DeviceError("No bootable device found") - else: - return bootDev + return mntDict.get("/boot/efi") def bootloaderChoices(self, bl): bootDev = self.bootDevice() @@ -234,10 +229,7 @@ class IPSeriesPPC(PPC): bootDev = device break - if not bootDev: - raise DeviceError("No bootable device found") - else: - return bootDev + return bootDev def bootloaderChoices(self, bl): ret = {} @@ -283,10 +275,7 @@ class NewWorldPPC(PPC): if device.format.type == "hfs" and device.bootable: bootDev = device - if not bootDev: - raise DeviceError("No bootable device found") - else: - return bootDev + return bootDev def bootloaderChoices(self, bl): ret = {} -- cgit From 7b834d7f281f384824309b579ba6c318e4545cdc Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Wed, 4 Mar 2009 11:21:40 -1000 Subject: Center resize window on the screen. --- ui/autopart.glade | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/autopart.glade b/ui/autopart.glade index 199c3591d..ae6909672 100644 --- a/ui/autopart.glade +++ b/ui/autopart.glade @@ -343,7 +343,7 @@ True Which Partition to resize GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE + GTK_WIN_POS_CENTER False True False -- cgit From 53588ad94f92e2e3d08780dda12b943b22ff46ba Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 5 Mar 2009 09:44:58 -1000 Subject: Fix StorageDevice.minSize() and PartitionDevice.maxSize() 1) Return the format minSize in StorageDevice.minSize() if we have it, do not compare to self.size. 2) PartitionDevice.maxSize() cannot use 'max' as a variable name since that's a built-in function. --- storage/devices.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index c97ce7f96..84cfe1bb2 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -482,7 +482,7 @@ class StorageDevice(Device): if self.exists: self.setup() - if self.format.minSize < self.size: + if self.format.minSize: return self.format.minSize else: return self.size @@ -1072,10 +1072,10 @@ class PartitionDevice(StorageDevice): def maxSize(self): """ The maximum size this partition can be. """ # XXX: this is MB by default - max = self.partedPartition.getMaxAvailableSize() + maxPartSize = self.partedPartition.getMaxAvailableSize() - if self.format.maxSize > max: - return max + if self.format.maxSize > maxPartSize: + return maxPartSize else: return self.format.maxSize -- cgit From 54aa4023f6251f1c198895c4ebf72e084f6f5c0a Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 5 Mar 2009 09:29:42 -1000 Subject: Ext2FS.migratable(self) -> Ext2FS.migratable Parent properties are not called like methods, remove '.(self)'. --- storage/formats/fs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index c1eebe409..90743d6bf 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -729,7 +729,7 @@ class Ext3FS(Ext2FS): def migratable(self): """ Can filesystems of this type be migrated? """ return (flags.cmdline.has_key("ext4migrate") and - Ext2FS.migratable(self)) + Ext2FS.migratable) register_device_format(Ext3FS) -- cgit From 7e7f76f1fb15b5a57e17fe2dcaa7d6b0c5a4e3a9 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 5 Mar 2009 09:52:50 -1000 Subject: Add fsckProg property to class FS. --- storage/formats/fs.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 90743d6bf..d861fd044 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -526,6 +526,11 @@ class FS(DeviceFormat): """ Program used to create filesystems of this type. """ return self._mkfs + @property + def fsckProg(self): + """ Program used to check filesystems of this type. """ + return self._fsck + @property def resizefsProg(self): """ Program used to resize filesystems of this type. """ -- cgit From a36096db370416cf336181d8b71428ebec8fb44b Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 5 Mar 2009 09:54:32 -1000 Subject: Fill out class NTFS a bit more. Add _fsck, _maxSize, _defaultCheckOptions. Make minSize a property. Uncomment _defaultMountOptions and make it a list. Uncomment _packages. --- storage/formats/fs.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index d861fd044..0f29aa63f 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -906,11 +906,15 @@ class NTFS(FS): """ ntfs filesystem. """ _type = "ntfs" _resizefs = "ntfsresize" + _fsck = "ntfsresize" _resizable = True _minSize = 1 - _defaultMountOptions = "defaults" - #_packages = ["ntfsprogs"] + _maxSize = 16 * 1024 * 1024 + _defaultMountOptions = ["defaults"] + _defaultCheckOptions = ["-c"] + _packages = ["ntfsprogs"] + @property def minSize(self): """ The minimum filesystem size in megabytes. """ size = self._minSize -- cgit From 36fb5d8e721a32979bc40a999eeee9445eaeaca9 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 5 Mar 2009 10:35:02 -1000 Subject: Add resizeArgs property for resizable filesystems. --- storage/formats/fs.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 0f29aa63f..d67dc94d4 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -300,8 +300,9 @@ class FS(DeviceFormat): # instance of the new filesystem type. self._type = self.migrationTarget - def _getResizeArgs(self): - argv = [self.device, self.targetSize] + @property + def resizeArgs(self): + argv = [self.device, "%d" % (self.targetSize,)] return argv def doResize(self, *args, **kwargs): @@ -336,8 +337,6 @@ class FS(DeviceFormat): self.doCheck(intf=intf) - argv = self._getResizeArgs() - w = None if intf: w = intf.progressWindow(_("Resizing"), @@ -347,7 +346,7 @@ class FS(DeviceFormat): try: rc = iutil.execWithPulseProgress(self.resizefsProg, - argv, + self.resizeArgs, stdout="/dev/tty5", stderr="/dev/tty5", progress=w) @@ -800,8 +799,9 @@ class BTRFS(FS): argv.append(self.device) return argv - def _getResizeArgs(self): - argv = ["-r", self.targetSize, self.device] + @property + def resizeArgs(self): + argv = ["-r", "%d" % (self.targetSize,), self.device] return argv @property @@ -941,6 +941,14 @@ class NTFS(FS): return size + @property + def resizeArgs(self): + # You must supply at least two '-f' options to ntfsresize or + # the proceed question will be presented to you. + argv = ["-ff", "-s", "%dM" % (self.targetSize,), self.device] + return argv + + register_device_format(NTFS) -- cgit From dc0124a15311d24cce4cae3610481826e6953d80 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 13:49:59 -0600 Subject: Handle unformatted disks and cdroms in Storage.exceptionDisks. --- storage/__init__.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/storage/__init__.py b/storage/__init__.py index c59b3bbee..bd3c7af8e 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -26,6 +26,9 @@ import stat import errno import sys +import parted +import _ped + import isys import iutil from constants import * @@ -386,7 +389,7 @@ class Storage(object): try: dev = parted.Device(path=device.path) disk = parted.Disk(device=dev) - except: + except (parted.DeviceException, _ped.DeviceException): continue for part in disk.partitions: -- cgit From 9219c98bd907a3cfb3acf0daf33cc2715647beef Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 15:53:35 -0600 Subject: Prevent clobbering of name 'mdraid' by qualifying it. --- iw/raid_dialog_gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index c9d99a24c..0b9d29573 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -28,7 +28,7 @@ import gtk import datacombo import gui -from storage.devicelibs import mdraid +import storage.devicelibs.mdraid from storage.devices import * from storage.deviceaction import * from partition_ui_helpers_gui import * @@ -418,7 +418,7 @@ class RaidEditor: if not origrequest.exists: - self.levelcombo = self.createRaidLevelMenu(mdraid.raid_levels, + self.levelcombo = self.createRaidLevelMenu(storage.devicelibs.mdraid.raid_levels, origrequest.level) lbl.set_mnemonic_widget(self.levelcombo) else: -- cgit From a0f7a517efb7a9d07560aee4231bb3789c033320 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 15:54:38 -0600 Subject: Make sure we end up with a disk that matches the chosen free region. --- storage/__init__.py | 61 +++++++++++++++++++++++++++++++++++++++++-------- storage/partitioning.py | 11 +++++++-- 2 files changed, 61 insertions(+), 11 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index bd3c7af8e..e4d6256a4 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1067,6 +1067,41 @@ class FSSet(object): self.cryptTab = None self.blkidTab = None self.active = False + self._tmpfs = None + self._sysfs = None + self._proc = None + self._devshm = None + + @property + def sysfs(self): + self._sysfs = self.mountpoints.get("/sys") + if not self._sysfs: + self._sysfs = NoDevice(format=getFormat("sysfs", device="/sys") + return self._sysfs + + @property + def devpts(self): + self._devpts = self.mountpoints.get("/dev/pts") + if not self._devpts: + self._devpts = NoDevice(format=getFormat("devpts", + device="/dev/pts") + return self._devpts + + @property + def proc(self): + self._proc = self.mountpoints.get("/proc") + if not self._proc: + self._proc = NoDevice(format=getFormat("proc", + device="/proc") + return self._proc + + @property + def devshm(self): + self._devshm = self.mountpoints.get("/dev/shm") + if not self._devshm: + self._devshm = NoDevice(format=getFormat("tmpfs", + device="/dev/shm") + return self._devshm @property def devices(self): @@ -1301,7 +1336,13 @@ class FSSet(object): def mountFilesystems(self, anaconda, raiseErrors=None, readOnly=None, skipRoot=False): intf = anaconda.intf - for device in [d for d in self.devices if d.isleaf]: + devices = self.mountpoints.values() + self.swapDevices + devices += self.devshm + devices += self.devpts + devices += self.sysfs + devices += self.proc + + for device in devices: if not device.format.mountable or not device.format.mountpoint: continue @@ -1392,10 +1433,13 @@ class FSSet(object): if os.path.ismount("%s/dev" % instPath): isys.umount("%s/dev" % instPath, removeDir=0) - # reverse works in place so we take a slice - devices = self.devices[:] + devices = self.mountpoints.values() + self.swapDevices + devices += self.devshm + devices += self.devpts + devices += self.sysfs + devices += self.proc devices.reverse() - for device in [d for d in devices if d.isleaf]: + for device in devices: if not device.format.mountable and \ (device.format.type != "swap" or swapoff): continue @@ -1523,10 +1567,6 @@ class FSSet(object): return conf - def writeFSTab(self, chroot="/"): - """ Write out /etc/fstab. """ - pass - def fstab (self): format = "%-23s %-23s %-7s %-15s %d %d\n" fstab = """ @@ -1539,8 +1579,11 @@ class FSSet(object): # """ % time.asctime() - devices = self.mountpoints.values() + self.swapDevices + devices += self.devshm + devices += self.devpts + devices += self.sysfs + devices += self.proc for device in devices: # why the hell do we put swap in the fstab, anyway? if not device.format.mountable and device.format.type != "swap": diff --git a/storage/partitioning.py b/storage/partitioning.py index a69751a37..b4f1ce1b7 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -588,6 +588,7 @@ def allocatePartitions(disks, partitions): log.debug("allocating partition: %s ; disks: %s ; boot: %s ; primary: %s ; size: %dMB ; grow: %s ; max_size: %s" % (_part.name, req_disks, _part.req_bootable, _part.req_primary, _part.req_size, _part.req_grow, _part.req_max_size)) free = None + use_disk = None # loop through disks for _disk in req_disks: disk = partedDisks[_disk.path] @@ -603,11 +604,14 @@ def allocatePartitions(disks, partitions): log.debug("checking freespace on %s" % _disk.name) - part_type = getNextPartitionType(disk) - if part_type is None: + new_part_type = getNextPartitionType(disk) + if new_part_type is None: # can't allocate any more partitions on this disk log.debug("no free partition slots on %s" % _disk.name) continue + else: + part_type = new_part_type + use_disk = _disk if _part.req_primary and part_type != parted.PARTITION_NORMAL: # we need a primary slot and none are free on this disk @@ -644,6 +648,9 @@ def allocatePartitions(disks, partitions): if free is None: raise PartitioningError("not enough free space on disks") + _disk = use_disk + disk = _disk.partedDisk + # create the extended partition if needed # TODO: move to a function (disk, free) if part_type == parted.PARTITION_EXTENDED: -- cgit From 26590efc55378c55d1910a7328d1424486ce95ec Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 15:59:10 -0600 Subject: Revert "Make sure we end up with a disk that matches the chosen free region." This reverts commit 6446254b60bfacf99a1cd5ce2d7a09061ac05135. --- storage/__init__.py | 61 ++++++++----------------------------------------- storage/partitioning.py | 11 ++------- 2 files changed, 11 insertions(+), 61 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index e4d6256a4..bd3c7af8e 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1067,41 +1067,6 @@ class FSSet(object): self.cryptTab = None self.blkidTab = None self.active = False - self._tmpfs = None - self._sysfs = None - self._proc = None - self._devshm = None - - @property - def sysfs(self): - self._sysfs = self.mountpoints.get("/sys") - if not self._sysfs: - self._sysfs = NoDevice(format=getFormat("sysfs", device="/sys") - return self._sysfs - - @property - def devpts(self): - self._devpts = self.mountpoints.get("/dev/pts") - if not self._devpts: - self._devpts = NoDevice(format=getFormat("devpts", - device="/dev/pts") - return self._devpts - - @property - def proc(self): - self._proc = self.mountpoints.get("/proc") - if not self._proc: - self._proc = NoDevice(format=getFormat("proc", - device="/proc") - return self._proc - - @property - def devshm(self): - self._devshm = self.mountpoints.get("/dev/shm") - if not self._devshm: - self._devshm = NoDevice(format=getFormat("tmpfs", - device="/dev/shm") - return self._devshm @property def devices(self): @@ -1336,13 +1301,7 @@ class FSSet(object): def mountFilesystems(self, anaconda, raiseErrors=None, readOnly=None, skipRoot=False): intf = anaconda.intf - devices = self.mountpoints.values() + self.swapDevices - devices += self.devshm - devices += self.devpts - devices += self.sysfs - devices += self.proc - - for device in devices: + for device in [d for d in self.devices if d.isleaf]: if not device.format.mountable or not device.format.mountpoint: continue @@ -1433,13 +1392,10 @@ class FSSet(object): if os.path.ismount("%s/dev" % instPath): isys.umount("%s/dev" % instPath, removeDir=0) - devices = self.mountpoints.values() + self.swapDevices - devices += self.devshm - devices += self.devpts - devices += self.sysfs - devices += self.proc + # reverse works in place so we take a slice + devices = self.devices[:] devices.reverse() - for device in devices: + for device in [d for d in devices if d.isleaf]: if not device.format.mountable and \ (device.format.type != "swap" or swapoff): continue @@ -1567,6 +1523,10 @@ class FSSet(object): return conf + def writeFSTab(self, chroot="/"): + """ Write out /etc/fstab. """ + pass + def fstab (self): format = "%-23s %-23s %-7s %-15s %d %d\n" fstab = """ @@ -1579,11 +1539,8 @@ class FSSet(object): # """ % time.asctime() + devices = self.mountpoints.values() + self.swapDevices - devices += self.devshm - devices += self.devpts - devices += self.sysfs - devices += self.proc for device in devices: # why the hell do we put swap in the fstab, anyway? if not device.format.mountable and device.format.type != "swap": diff --git a/storage/partitioning.py b/storage/partitioning.py index b4f1ce1b7..a69751a37 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -588,7 +588,6 @@ def allocatePartitions(disks, partitions): log.debug("allocating partition: %s ; disks: %s ; boot: %s ; primary: %s ; size: %dMB ; grow: %s ; max_size: %s" % (_part.name, req_disks, _part.req_bootable, _part.req_primary, _part.req_size, _part.req_grow, _part.req_max_size)) free = None - use_disk = None # loop through disks for _disk in req_disks: disk = partedDisks[_disk.path] @@ -604,14 +603,11 @@ def allocatePartitions(disks, partitions): log.debug("checking freespace on %s" % _disk.name) - new_part_type = getNextPartitionType(disk) - if new_part_type is None: + part_type = getNextPartitionType(disk) + if part_type is None: # can't allocate any more partitions on this disk log.debug("no free partition slots on %s" % _disk.name) continue - else: - part_type = new_part_type - use_disk = _disk if _part.req_primary and part_type != parted.PARTITION_NORMAL: # we need a primary slot and none are free on this disk @@ -648,9 +644,6 @@ def allocatePartitions(disks, partitions): if free is None: raise PartitioningError("not enough free space on disks") - _disk = use_disk - disk = _disk.partedDisk - # create the extended partition if needed # TODO: move to a function (disk, free) if part_type == parted.PARTITION_EXTENDED: -- cgit From fa4cf01162a91444846c020ea0c1a21c54d2f13f Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 16:05:27 -0600 Subject: Make sure we use the same disk the free space is on. (#488807) --- storage/partitioning.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index a69751a37..b4f1ce1b7 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -588,6 +588,7 @@ def allocatePartitions(disks, partitions): log.debug("allocating partition: %s ; disks: %s ; boot: %s ; primary: %s ; size: %dMB ; grow: %s ; max_size: %s" % (_part.name, req_disks, _part.req_bootable, _part.req_primary, _part.req_size, _part.req_grow, _part.req_max_size)) free = None + use_disk = None # loop through disks for _disk in req_disks: disk = partedDisks[_disk.path] @@ -603,11 +604,14 @@ def allocatePartitions(disks, partitions): log.debug("checking freespace on %s" % _disk.name) - part_type = getNextPartitionType(disk) - if part_type is None: + new_part_type = getNextPartitionType(disk) + if new_part_type is None: # can't allocate any more partitions on this disk log.debug("no free partition slots on %s" % _disk.name) continue + else: + part_type = new_part_type + use_disk = _disk if _part.req_primary and part_type != parted.PARTITION_NORMAL: # we need a primary slot and none are free on this disk @@ -644,6 +648,9 @@ def allocatePartitions(disks, partitions): if free is None: raise PartitioningError("not enough free space on disks") + _disk = use_disk + disk = _disk.partedDisk + # create the extended partition if needed # TODO: move to a function (disk, free) if part_type == parted.PARTITION_EXTENDED: -- cgit From ea1c840d5b43f4125d9208cf5e1fd5b4bcd89814 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 16:17:15 -0600 Subject: Fix incomplete format in Storage.deviceImmutable. --- storage/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/__init__.py b/storage/__init__.py index bd3c7af8e..d5c8db2a5 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -433,7 +433,7 @@ class Storage(object): if array.dependsOn(device): if array.minor is not None: return _("This device is part of the RAID " - "device %.") % (array.path,) + "device %s.") % (array.path,) else: return _("This device is part of a RAID device.") elif device.format.type == "lvmpv": -- cgit From 0b142a9861395859e05f4430bdcdd91f31cd4b8f Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 16:30:43 -0600 Subject: Add properties to FSSet to provide the nodev entries. --- storage/__init__.py | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/storage/__init__.py b/storage/__init__.py index d5c8db2a5..0c580c6e6 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1067,6 +1067,46 @@ class FSSet(object): self.cryptTab = None self.blkidTab = None self.active = False + self._tmpfs = None + self._sysfs = None + self._proc = None + self._devshm = None + + @property + def sysfs(self): + self._sysfs = self.mountpoints.get("/sys") + if not self._sysfs: + self._sysfs = NoDevice(format=getFormat("sysfs", + device="sys", + mountpoint="/sys")) + return self._sysfs + + @property + def devpts(self): + self._devpts = self.mountpoints.get("/dev/pts") + if not self._devpts: + self._devpts = NoDevice(format=getFormat("devpts", + device="devpts", + mountpoint="/dev/pts")) + return self._devpts + + @property + def proc(self): + self._proc = self.mountpoints.get("/proc") + if not self._proc: + self._proc = NoDevice(format=getFormat("proc", + device="proc", + mountpoint="/proc")) + return self._proc + + @property + def devshm(self): + self._devshm = self.mountpoints.get("/dev/shm") + if not self._devshm: + self._devshm = NoDevice(format=getFormat("tmpfs", + device="tmpfs", + mountpoint="/dev/shm")) + return self._devshm @property def devices(self): -- cgit From 9192f261f83d825c70e1e152677aac73a1bf08fd Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 16:32:27 -0600 Subject: Remove FSSet.writeFSTab. That job is handled elsewhere. --- storage/__init__.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 0c580c6e6..94b126df4 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1563,10 +1563,6 @@ class FSSet(object): return conf - def writeFSTab(self, chroot="/"): - """ Write out /etc/fstab. """ - pass - def fstab (self): format = "%-23s %-23s %-7s %-15s %d %d\n" fstab = """ -- cgit From d9adfe0d0a08ce045a4e78a191c64fd7aafd3761 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 16:41:29 -0600 Subject: Include proc, &c filesystems in fstab and FSSet.{mount/umount}Filesystems. --- storage/__init__.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 94b126df4..7bac367dc 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1341,7 +1341,10 @@ class FSSet(object): def mountFilesystems(self, anaconda, raiseErrors=None, readOnly=None, skipRoot=False): intf = anaconda.intf - for device in [d for d in self.devices if d.isleaf]: + devices = self.mountpoints.values() + self.swapDevices + devices.extend([self.devshm, self.devpts, self.sysfs, self.proc]) + devices.sort(key=lambda d: getattr(d.format, "mountpoint", None)) + for device in devices: if not device.format.mountable or not device.format.mountpoint: continue @@ -1432,10 +1435,11 @@ class FSSet(object): if os.path.ismount("%s/dev" % instPath): isys.umount("%s/dev" % instPath, removeDir=0) - # reverse works in place so we take a slice - devices = self.devices[:] + devices = self.mountpoints.values() + self.swapDevices + devices.extend([self.devshm, self.devpts, self.sysfs, self.proc]) + devices.sort(key=lambda d: getattr(d.format, "mountpoint", None)) devices.reverse() - for device in [d for d in devices if d.isleaf]: + for device in devices: if not device.format.mountable and \ (device.format.type != "swap" or swapoff): continue @@ -1575,8 +1579,8 @@ class FSSet(object): # """ % time.asctime() - devices = self.mountpoints.values() + self.swapDevices + devices.extend([self.devshm, self.devpts, self.sysfs, self.proc]) for device in devices: # why the hell do we put swap in the fstab, anyway? if not device.format.mountable and device.format.type != "swap": -- cgit From b3d1fc9cd3ded8c5cfaa272fab81336a7d9cf3d6 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 16:44:27 -0600 Subject: Nodev filesystems always exist. And the device is arbitrary. --- storage/formats/fs.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index d67dc94d4..2cc35bb67 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -980,9 +980,11 @@ class NoDevFS(FS): def __init__(self, *args, **kwargs): FS.__init__(self, *args, **kwargs) + self.exists = True + + def _setDevice(self, devspec): + self._device = devspec - def _deviceCheck(self, devspec): - pass class DevPtsFS(NoDevFS): """ devpts filesystem. """ -- cgit From 15c9e685f23ca30a411a989ca54a7311247f2e66 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 17:42:39 -0600 Subject: Pass storage.disks, not storage, to createAllowed.... (#488860) --- iw/autopart_type.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iw/autopart_type.py b/iw/autopart_type.py index bb3853323..ba8c0dc82 100644 --- a/iw/autopart_type.py +++ b/iw/autopart_type.py @@ -366,7 +366,7 @@ class PartitionTypeWindow(InstallWindow): w = self.intf.waitWindow(_("Rescanning disks"), _("Rescanning disks")) self.storage.reset() - createAllowedDrivesStore(self.storage, + createAllowedDrivesStore(self.storage.disks, self.storage.clearPartDisks, self.drivelist, disallowDrives=[self.anaconda.updateSrc]) -- cgit From ee37a416dda44c0b96e00d7b18338e530ffaf4b5 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 5 Mar 2009 18:30:21 -0600 Subject: Schedule device destroy actions for partitions last. --- storage/partitioning.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index b4f1ce1b7..d1f2d4d9c 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -241,7 +241,7 @@ def clearPartitions(storage): # we are only interested in partitions that physically exist partitions = [p for p in storage.partitions if p.exists] disks = [] # a list of disks from which we've removed partitions - + clearparts = [] # list of partitions we'll remove for part in partitions: log.debug("clearpart: looking at %s" % part.name) clear = False # whether or not we will clear this partition @@ -292,6 +292,9 @@ def clearPartitions(storage): if disk_name not in disks: disks.append(disk_name) + clearparts.append(part) + + for part in clearparts: storage.destroyDevice(part) # now remove any empty extended partitions -- cgit From 641ac3d2faca1df59f86ca5a0441559aa0d35488 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 5 Mar 2009 15:30:43 -1000 Subject: Merge in changes present in anaconda pkgcvs. --- anaconda.spec | 11 +++++-- scripts/mk-images.efi | 81 ++++++++++++++++++++++++++++++--------------------- scripts/mk-images.x86 | 11 +++++-- 3 files changed, 64 insertions(+), 39 deletions(-) diff --git a/anaconda.spec b/anaconda.spec index 5dc8a03c0..3a39b064a 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -3,7 +3,7 @@ Summary: Graphical system installer Name: anaconda Version: 11.5.0.24 -Release: 1 +Release: 3 License: GPLv2+ Group: Applications/System URL: http://fedoraproject.org/wiki/Anaconda @@ -41,7 +41,7 @@ BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) %define createrepover 0.4.7 %define yumutilsver 1.1.11-3 %define iscsiver 6.2.0.870-3 -%define pythoncryptsetupver 0.0.7 +%define pythoncryptsetupver 0.0.6 BuildRequires: audit-libs-devel BuildRequires: bzip2-devel @@ -209,6 +209,13 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Thu Mar 5 2009 Dave Lehman - 11.5.0.24-3 +- Fix booty's desire to import fsset. +- Fix attempt to set read-only attr "removable" in DiskDevice.__init__ + +* Thu Mar 05 2009 Peter Jones - 11.5.0.24-2 +- Add EFI boot.iso generation. + * Wed Mar 4 2009 Dave Lehman - 11.5.0.24-1 - Storage test day. diff --git a/scripts/mk-images.efi b/scripts/mk-images.efi index 687d76c01..7c2595033 100644 --- a/scripts/mk-images.efi +++ b/scripts/mk-images.efi @@ -53,25 +53,25 @@ makeefibootimage() { MBD_BOOTTREE=${TMPDIR:-/tmp}/makebootdisk.tree.$$ MBD_BOOTTREE_TMP=$MBD_BOOTTREE'_tmp' while [ x$(echo $1 | cut -c1-2) = x"--" ]; do - if [ $1 = "--kernel" ]; then - KERNELFILE=$2 - shift; shift - continue - elif [ $1 = "--initrd" ]; then - INITRDFILE=$2 - shift; shift - continue - elif [ $1 = "--imagename" ]; then - MBD_FILENAME=$IMAGEPATH/$2 - shift; shift - continue - elif [ $1 = "--grubpkg" ]; then - grubpkg=$2 - shift; shift - continue - fi - echo "Unknown option passed to makebootdisk" - exit 1 + if [ $1 = "--kernel" ]; then + KERNELFILE=$2 + shift; shift + continue + elif [ $1 = "--initrd" ]; then + INITRDFILE=$2 + shift; shift + continue + elif [ $1 = "--imagename" ]; then + MBD_FILENAME=$IMAGEPATH/$2 + shift; shift + continue + elif [ $1 = "--grubpkg" ]; then + grubpkg=$2 + shift; shift + continue + fi + echo "Unknown option passed to makebootdisk" + exit 1 done if [ -z "$MBD_FILENAME" ]; then @@ -79,15 +79,6 @@ makeefibootimage() { exit 1 fi - if [ -z "$KERNELFILE" ]; then - echo "No kernel file passed" - exit 1 - fi - - if [ -z "$INITRDFILE" ]; then - echo "No initrd file passed" - exit 1 - fi MBD_FSIMAGE="$INITRDFILE" mkdir -p $MBD_BOOTTREE @@ -110,7 +101,7 @@ makeefibootimage() { mkdir -p `dirname $MBD_FILENAME` rm -rf $MBD_TMPIMAGE $MBD_MNTPOINT $MBD_BOOTTREE - if [ -z "$INITRDFILE" ]; then + if [ -z "$INITRDFILE" -a -n "$MBD_FSIMAGE" ]; then rm -f $MBD_FSIMAGE fi @@ -137,8 +128,8 @@ prepareEfiTree() { mkdir -p $MBD_BOOTTREE_TMP/EFI/boot cp -a $BOOTDISKDIR/* $MBD_BOOTTREE_TMP/EFI/boot/ - cp $INITRDFILE $MBD_BOOTTREE_TMP/EFI/boot/initrd.img - cp $KERNELFILE $MBD_BOOTTREE_TMP/EFI/boot/vmlinuz + [ -n "$INITRDFILE" ] && cp $INITRDFILE $MBD_BOOTTREE_TMP/EFI/boot/initrd.img + [ -n "$KERNELFILE" ] && cp $KERNELFILE $MBD_BOOTTREE_TMP/EFI/boot/vmlinuz sed -i "s/@PRODUCT@/$PRODUCT/g" $MBD_BOOTTREE_TMP/EFI/boot/grub.conf sed -i "s/@VERSION@/$VERSION/g" $MBD_BOOTTREE_TMP/EFI/boot/grub.conf @@ -165,10 +156,18 @@ prepareEfiTree() { yumdownloader -c ${yumconf} ${artpkg} rpm2cpio ${artpkg}.rpm | (cd $KERNELROOT; cpio --quiet -iumd) cp $KERNELROOT/boot/grub/splash.xpm.gz $MBD_BOOTTREE_TMP/EFI/boot/splash.xpm.gz + + # if we don't have a kernel or initrd, we're making a CD image and we need + # to mirror EFI/ to the cd. + if [ -z "$KERNELFILE" -a -z "$INITRDFILE" ]; then + cp -av $MBD_BOOTTREE_TMP/EFI/ $TOPDESTPATH/EFI/ + rm -f $TOPDESTPATH/EFI/boot/*.efi + fi } makeEfiImages() { yumconf="$1" + echo "Making EFI images ($PWD)" if [ "$kernelvers" != "$kernelxen" ]; then local grubarch=${efiarch} case ${efiarch} in @@ -190,10 +189,24 @@ makeEfiImages() { --initrd $TOPDESTPATH/images/pxeboot/initrd.img \ --grubpkg ${grubpkg} local ret=$? - [ $ret -eq 0 ] || return $ret + if [ $ret -ne 0 ]; then + echo "makeefibootimage (1) failed" >&2 + return $ret + fi makeefibootdisk $TOPDESTPATH/images/pxeboot/efiboot.img $TOPDESTPATH/images/efidisk.img - return $? + [ $ret -eq 0 ] || return $ret + local ret=$? + + # make a boot image with just boot*.efi in it... + makeefibootimage \ + --imagename pxeboot/efiboot.img \ + --grubpkg ${grubpkg} + local ret=$? + if [ $ret -ne 0 ]; then + echo "makeefibootimage (2) failed" >&2 + fi + return $ret fi - return 1 + return 0 } diff --git a/scripts/mk-images.x86 b/scripts/mk-images.x86 index 813d94185..fe7a978b9 100644 --- a/scripts/mk-images.x86 +++ b/scripts/mk-images.x86 @@ -158,11 +158,16 @@ __EOT__ doPostImages() { if [ -n "$BOOTISO" ]; then EFIARGS="" - if [ -f isolinux/efiboot.img ]; then + EFIGRAFT="" + if [ -f $TOPDESTPATH/images/pxeboot/efiboot.img ]; then + cp $TOPDESTPATH/images/pxeboot/efiboot.img $TOPDESTPATH/isolinux/efiboot.img EFIARGS="-eltorito-alt-boot -e isolinux/efiboot.img -no-emul-boot" + EFIGRAFT="EFI/boot=$TOPDESTPATH/EFI/boot" fi - mkisofs -o $TOPDESTPATH/images/$BOOTISO -b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table $EFIARGS -R -J -V "$PRODUCT" -T -graft-points isolinux=$TOPDESTPATH/isolinux images/install.img=$TOPDESTPATH/images/install.img - implantisomd5 $TOPDESTPATH/images/$BOOTISO + BIOSARGS="-b isolinux/isolinux.bin -c isolinux/boot.cat -no-emul-boot -boot-load-size 4 -boot-info-table" + echo $PWD:\$ mkisofs -o $TOPDESTPATH/images/$BOOTISO $BIOSARGS $EFIARGS -R -J -V "$PRODUCT" -T -graft-points isolinux=$TOPDESTPATH/isolinux images/install.img=$TOPDESTPATH/images/install.img $EFIGRAFT + mkisofs -o $TOPDESTPATH/images/$BOOTISO $BIOSARGS $EFIARGS -R -J -V "$PRODUCT" -T -graft-points isolinux=$TOPDESTPATH/isolinux images/install.img=$TOPDESTPATH/images/install.img $EFIGRAFT + implantisomd5 $TOPDESTPATH/images/$BOOTISO fi } -- cgit From 19234de716690c8b38014a0867dc15e91577008c Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 5 Mar 2009 15:34:23 -1000 Subject: New version. --- anaconda.spec | 47 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/anaconda.spec b/anaconda.spec index 3a39b064a..c6ac69596 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,8 +2,8 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.24 -Release: 3 +Version: 11.5.0.25 +Release: 1 License: GPLv2+ Group: Applications/System URL: http://fedoraproject.org/wiki/Anaconda @@ -209,6 +209,49 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Thu Mar 05 2009 David Cantrell - 11.5.0.25-1 +- Schedule device destroy actions for partitions last. (dlehman) +- Pass storage.disks, not storage, to createAllowed.... (#488860) (dlehman) +- Nodev filesystems always exist. And the device is arbitrary. (dlehman) +- Include proc, &c filesystems in fstab and FSSet.{mount/umount}Filesystems. + (dlehman) +- Remove FSSet.writeFSTab. That job is handled elsewhere. (dlehman) +- Add properties to FSSet to provide the nodev entries. (dlehman) +- Fix incomplete format in Storage.deviceImmutable. (dlehman) +- Make sure we use the same disk the free space is on. (#488807) (dlehman) +- Prevent clobbering of name 'mdraid' by qualifying it. (dlehman) +- Handle unformatted disks and cdroms in Storage.exceptionDisks. (dlehman) +- Add resizeArgs property for resizable filesystems. (dcantrell) +- Fill out class NTFS a bit more. (dcantrell) +- Add fsckProg property to class FS. (dcantrell) +- Ext2FS.migratable(self) -> Ext2FS.migratable (dcantrell) +- Fix StorageDevice.minSize() and PartitionDevice.maxSize() (dcantrell) +- Center resize window on the screen. (dcantrell) +- Do not raise DeviceError if not bootable device is found. (dcantrell) +- Do an even more thorough job of ignoring disks libparted doesn't like. + (clumens) +- Fix a couple problems on the "Change device" bootloader dialog. (clumens) +- Fix a typo when writing out the mdadm config file. (clumens) +- Remove all uses of isys.cdromList, which no longer exists. (clumens) +- Check to see if we're on S390 on the congrats screen (#488747). (clumens) +- Handle non-fatal errors more gracefully in addUdevDevice. (dlehman) +- partRequests no longer exists, so don't try to import it (#488743). + (clumens) +- When building the exceptionDisks list, skip devices libparted doesn't + like. (clumens) +- Iterate over devicetree.devices.values, not devicetree. (dlehman) +- Add a get() method to Flags, since it pretends to be a dictionary. + (clumens) +- Stop with the fsset usage. (dlehman) +- Format message string after translation not before (msivak) +- We need newer python-cryptsetup because of the default values for cipher + and keysize for luskFormat (msivak) +- If a drive is not initialized, offer reinitialization or ignoring the + drive to the user (msivak) +- More syntax errors / traceback fixes (hdegoede) +- Fix syntax errors (rvykydal) +- Implement Storage.sanityCheck, mostly from old partitions code. (dlehman) + * Thu Mar 5 2009 Dave Lehman - 11.5.0.24-3 - Fix booty's desire to import fsset. - Fix attempt to set read-only attr "removable" in DiskDevice.__init__ -- cgit From 0b65aca74899a558d14d1e305bb98380be185dfc Mon Sep 17 00:00:00 2001 From: Martin Sivak Date: Fri, 6 Mar 2009 12:20:07 +0100 Subject: Do not pass None values into nonmandatory arguments, you are screwing the default values.. --- storage/devicelibs/crypto.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/storage/devicelibs/crypto.py b/storage/devicelibs/crypto.py index be919ff89..28da47c54 100644 --- a/storage/devicelibs/crypto.py +++ b/storage/devicelibs/crypto.py @@ -62,7 +62,15 @@ def luks_format(device, else: raise ValueError("luks_format requires either a passphrase or a key file") - rc = cs.luksFormat(device = device, cipher = cipher, keysize = key_size, keyfile = key_file) + #None is not considered as default value and pycryptsetup doesn't accept it + #so we need to filter out all Nones + kwargs = {} + kwargs["device"] = device + if cipher: kwargs["cipher"] = cipher + if key_file: kwargs["keyfile"] = key_file + if key_size: kwargs["keysize"] = key_size + + rc = cs.luksFormat(**kwargs) if key_file_unlink: os.unlink(key_file) if rc: -- cgit From 7b3a73f1018efad6f8a640dd61f8cc5a7571f69d Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Thu, 5 Mar 2009 18:10:16 +0100 Subject: Add dmraid functionality to new storage code. * storage/devices.py (DMRaidArrayDevice): Complete the DMRaidArrayDevice class. * storage/devices.py (PartitionDeviceFactory): When creating a partition we need to decide, based on the device holding the partition, what type of partition class will be returned. * storage/devices.py (PartitionDevice): When setting the disk we must be careful to change stuff accordingly. * storage/devicetree.py (addUdevDevice): Handle the creation of the dmraid device and its members in the addUdeveDevice function. * storage/formats/dmraid.py (DMRaidMember): Complete the format that handles devices being members of a dmraid array. * storage/udev.py (udev_device_is_dmraid): add a function that recognizes dmraid devices from the provided info list. * storage/udev.py (udev_device_get_dmraid_partition_disk): function that gets the disk string from partition info. * storage/udev.py (udev_device_is_dmraid_partition): function that checks for dmraid partitions. * storage/__init__.py (disks(self)): return a list of disks from the internal attribute. * storage/__init__.py (Storage.disks): we really want to make sure we return all the objects that comply with the DiskDevice interface (inheret from DiskDevice) * storage/__init__.py (Storage.partitions): Same as above but for PartitionDevice. * iw/autopart_type (PartitionTypeWindow(InstallWindow)): use disk.name instead of munging the first 5 characters of the path. * storage/partitioning (newPartition): Use the new partition factory to create a partition. --- iw/autopart_type.py | 9 +-- iw/partition_gui.py | 8 +- storage/__init__.py | 22 +++-- storage/devices.py | 202 ++++++++++++++++++++++++++++++++++++++++++---- storage/devicetree.py | 92 +++++++++++++++++---- storage/formats/dmraid.py | 23 ++++-- storage/partitioning.py | 4 +- storage/udev.py | 33 ++++++++ 8 files changed, 341 insertions(+), 52 deletions(-) diff --git a/iw/autopart_type.py b/iw/autopart_type.py index ba8c0dc82..700d7da0e 100644 --- a/iw/autopart_type.py +++ b/iw/autopart_type.py @@ -381,14 +381,11 @@ class PartitionTypeWindow(InstallWindow): else: defaultBoot = None for disk in self.storage.disks: - partedDisk = disk.partedDisk - if partedDisk.device.path[5:] not in self.anaconda.id.bootloader.drivelist: + if disk.name not in self.anaconda.id.bootloader.drivelist: continue - size = partedDisk.device.getSize(unit="MB") - dispstr = "%s %8.0f MB %s" %(partedDisk.device.path[5:], - size, partedDisk.device.model) + dispstr = "%s %8.0f MB %s" %(disk.name, disk.size, disk.partedDisk.device.model) i = bootstore.append(None) - bootstore[i] = (dispstr, partedDisk.device.path[5:]) + bootstore[i] = (dispstr, disk.name) if disk.name == defaultBoot: self.bootcombo.set_active_iter(i) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index c704b0893..9dae43657 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -142,12 +142,12 @@ class DiskStripeSlice: if self.partition.type & parted.PARTITION_FREESPACE: rc = "Free\n" else: - rc = "%s\n" % (self.partition.getDeviceNodeName(),) + rc = "%s\n" % (self.partition.getDeviceNodeName().split("/")[-1],) rc = rc + "%Ld MB" % (self.partition.getSize(unit="MB"),) return rc def getDeviceName(self): - return self.partition.getDeviceNodeName() + return self.partition.getDeviceNodeName().split("/")[-1] def update(self): disk = self.parent.getDisk() @@ -808,7 +808,7 @@ class PartitionWindow(InstallWindow): part = part.nextPartition() continue - partName = part.getDeviceNodeName() + partName = part.getDeviceNodeName().split("/")[-1] device = self.storage.devicetree.getDeviceByName(partName) if not device and not part.type & parted.PARTITION_FREESPACE: raise RuntimeError("can't find partition %s in device" @@ -1054,7 +1054,7 @@ class PartitionWindow(InstallWindow): self.editLVMVolumeGroup(device) elif device.type == "lvmlv": self.editLVMVolumeGroup(device) - elif device.type == "partition": + elif isinstance(device, storage.devices.PartitionDevice): self.editPartition(device) # isNew implies that this request has never been successfully used before diff --git a/storage/__init__.py b/storage/__init__.py index 7bac367dc..6c241c2c0 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -238,7 +238,11 @@ class Storage(object): does not necessarily reflect the actual on-disk state of the system's disks. """ - disks = self.devicetree.getDevicesByType("disk") + disks = [] + devices = self.devicetree.devices + for d in devices: + if isinstance(devices[d], DiskDevice): + disks.append(devices[d]) disks.sort(key=lambda d: d.name) return disks @@ -250,7 +254,11 @@ class Storage(object): does not necessarily reflect the actual on-disk state of the system's disks. """ - partitions = self.devicetree.getDevicesByType("partition") + partitions = [] + devices = self.devicetree.devices + for d in devices: + if isinstance(devices[d], PartitionDevice): + partitions.append(devices[d]) partitions.sort(key=lambda d: d.name) return partitions @@ -423,7 +431,7 @@ class Storage(object): if device.name in self.protectedPartitions: return _("This partition is holding the data for the hard " "drive install.") - elif device.type == "partition" and device.isProtected: + elif isinstance(device, PartitionDevice) and device.isProtected: # LDL formatted DASDs always have one partition, you'd have to # reformat the DASD in CDL mode to get rid of it return _("You cannot delete a partition of a LDL formatted " @@ -445,7 +453,7 @@ class Storage(object): else: return _("This device is part of a LVM volume " "group.") - elif device.type == "partition" and device.isExtended: + elif isinstance(device, PartitionDevice) and device.isExtended: reasons = {} for dep in self.deviceDeps(device): reason = self.deviceImmutable(dep) @@ -473,13 +481,17 @@ class Storage(object): if kwargs.has_key("disks"): parents = kwargs.pop("disks") + if isinstance(parents, Device): + kwargs["parents"] = [parents] + else: + kwargs["parents"] = parents if kwargs.has_key("name"): name = kwargs.pop("name") else: name = "req%d" % self.nextID - return PartitionDevice(name, *args, **kwargs) + return PartitionDeviceFactory(name, *args, **kwargs) def newMDArray(self, *args, **kwargs): """ Return a new MDRaidArrayDevice instance for configuring. """ diff --git a/storage/devices.py b/storage/devices.py index 84cfe1bb2..dd5a4f19d 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -127,6 +127,74 @@ def get_device_majors(): return majors device_majors = get_device_majors() +def PartitionDeviceFactory(*args, **kwargs): + """This will decide what PartitionDevice class will be used. + + Arguments: + + name -- the device name (generally a device node's basename) + + Keyword Arguments: + + exists -- indicates whether this is an existing device + format -- the device's format (DeviceFormat instance) + + For existing partitions: + + parents -- the disk that contains this partition + major -- the device major + minor -- the device minor + sysfsPath -- sysfs device path + + For new partitions: + + partType -- primary,extended,&c (as parted constant) + grow -- whether or not to grow the partition + maxsize -- max size for growable partitions (in MB) + size -- the device's size (in MB) + bootable -- whether the partition is bootable + parents -- a list of potential containing disks + + The decision will be made base on finding the disk by name + """ + # FIXME: PRePBootDevice should be here somehow.!!! + roots = ["/dev", "/dev/mapper"] + # firs lets normalize the name + norm_name = args[0].split("/")[-1] + + # We look for the disk in /dev. From PartitionDevice its the [0] one. + if (not kwargs.has_key("parents")) or\ + (kwargs.has_key("exists") and not kwargs["exists"]): + # Cant really choose a good type of class, default to PartitionDevice + # This will be considered as a request. + return PartitionDevice(*args, **kwargs) + else: + parents = kwargs["parents"] + if isinstance(parents, Device): + parents = [parents] + # we receive a list of parents. look for the disk that contains the name + # if we dont find the name we will return PartitionDevice and let it raise + # the exception. + for root in roots: + for parent in parents: + path = os.path.join(root,norm_name) + log.debug("looking up parted Partition: %s" % path) + part = parent.partedDisk.getPartitionByPath(path) + if not part: + continue + else: + # we have found a part. no make the decision based on path + if path.startswith(roots[1]): + #we create a DMPartition + return DMRaidPartitionDevice(*args, **kwargs) + elif path.startswith(roots[0]): + # make sure that the parent is consistent + kwargs["parents"] = [parent] + return PartitionDevice(*args, **kwargs) + + # If we get here, we did not find anything. + return PartitionDevice(*args, **kwargs) + class Device(object): """ A generic device. @@ -861,14 +929,14 @@ class PartitionDevice(StorageDevice): # no need to clobber our name else: self._partedPartition = partition - self._name = partition.getDeviceNodeName() + self._name = partition.getDeviceNodeName().split("/")[-1] partedPartition = property(lambda d: d._getPartedPartition(), lambda d,p: d._setPartedPartition(p)) def dependsOn(self, dep): """ Return True if this device depends on dep. """ - if dep.type == "partition" and dep.isExtended and self.isLogical: + if isinstance(dep, PartitionDevice) and dep.isExtended and self.isLogical: return True return Device.dependsOn(self, dep) @@ -1051,7 +1119,7 @@ class PartitionDevice(StorageDevice): geometry.length = new_length def _getDisk(self): - """ The disk that contains this partition. """ + """ The disk that contains this partition.""" try: disk = self.parents[0] except IndexError: @@ -1059,12 +1127,27 @@ class PartitionDevice(StorageDevice): return disk def _setDisk(self, disk): + """Change the parent. + + Setting up a disk is not trivial. It has the potential to change + the underlying object. If necessary we must also change this object. + """ log_method_call(self, self.name, old=self.disk, new=disk) if self.disk: self.disk.removeChild() self.parents = [disk] disk.addChild() + if isinstance(disk, DMRaidArrayDevice): + # modify the partition so it can look like the DMRaidPartitionDevice. + + self._type = "dmraid partition" + self._packages = ["dmraid"] + self.devDir = "/dev/mapper" + self.updateSysfsPath = DMDevice.updateSysfsPath + self.getDMNode = DMDevice.getDMNode + + disk = property(lambda p: p._getDisk(), lambda p,d: p._setDisk(d)) @@ -2069,41 +2152,130 @@ class MDRaidArrayDevice(StorageDevice): self.exists = False -class DMRaidArrayDevice(DMDevice): +class DMRaidArrayDevice(DiskDevice): """ A dmraid (device-mapper RAID) device """ _type = "dm-raid array" _packages = ["dmraid"] + devDir = "/dev/mapper" - def __init__(self, name, format=None, size=None, - exists=None, parents=None, sysfsPath=''): + def __init__(self, name, raidSet=None, level=None, format=None, size=None, + major=None, minor=None, parents=None, sysfsPath=''): """ Create a DMRaidArrayDevice instance. Arguments: - name -- the device name (generally a device node's basename) + name -- the dmraid name also the device node's basename Keyword Arguments: + raidSet -- the RaidSet object from block + level -- the type of dmraid parents -- a list of the member devices sysfsPath -- sysfs device path size -- the device's size format -- a DeviceFormat instance - exists -- indicates whether this is an existing device """ if isinstance(parents, list): for parent in parents: if not parent.format or parent.format.type != "dmraidmember": raise ValueError("parent devices must contain dmraidmember format") - DMDevice.__init__(self, name, format=format, size=size, - parents=parents, sysfsPath=sysfsPath, - exists=exists) + DiskDevice.__init__(self, name, format=format, size=size, + major=major, minor=minor, + parents=parents, sysfsPath=sysfsPath) - def probe(self): - """ Probe for any missing information about this device. + self.formatClass = get_device_format_class("dmraidmember") + if not self.formatClass: + raise DeviceError("cannot find class for 'dmraidmember'") - size + + self._raidSet = raidSet + self._level = level + + @property + def raidSet(self): + return self._raidSet + + @raidSet.setter + def raidSet(self, val): + # If we change self._raidSet, parents list will be invalid. + # Don't allow the change. + pass + + def _addDevice(self, device): + """ Add a new member device to the array. + + XXX This is for use when probing devices, not for modification + of arrays. """ - raise NotImplementedError("probe method not defined for DMRaidArrayDevice") + log_method_call(self, self.name, device=device.name, status=self.status) + + if not self.exists: + raise DeviceError("device has not been created") + + if not isinstance(device.format, self.formatClass): + raise ValueError("invalid device format for dmraid member") + + if device in self.members: + raise ValueError("device is already a member of this array") + + # we added it, so now set up the relations + self.devices.append(device) + device.addChild() + + @property + def members(self): + return self.parents + + @property + def devices(self): + """ Return a list of this array's member device instances. """ + return self.parents + + def activate(self, mknod=True): + self._raidSet.activate(mknod=mknod) + + def deactivate(self): + self._raidSet.deactivate() + + # We are more like a disk then a dmdevice, but we are still a dmdevice, + # so we need to "inherit" some functions from dmdevice + def updateSysfsPath(self): + DMDevice.updateSysfsPath(self) + + def getDMNode(self): + DMDevice.getDMNode(self) + + def teardown(self, recursive=None): + # avoid DiskDevice's overriding of teardown() + StorageDevice.teardown(self, recursive) + + +class DMRaidPartitionDevice(PartitionDevice): + """ A disk partition in a dmraid array, identical to a general + PartitionDevice, except for the device path and sysfs path + """ + _type = "dmraid partition" + _packages = ["dmraid"] + devDir = "/dev/mapper" + + def __init__(self, name, format=None, + size=None, grow=False, maxsize=None, + major=None, minor=None, bootable=None, + sysfsPath='', parents=None, exists=None, + partType=None, primary=False): + PartitionDevice.__init__(self, name, format=format, size=size, + major=major, minor=minor, bootable=bootable, + sysfsPath=sysfsPath, parents=parents, + exists=exists, partType=partType, + primary=primary) + + # We are more like a partition then a dmdevice, but we are still a dmdevice + # so we need to "inherit" some functions from dmdevice + def updateSysfsPath(self): + DMDevice.updateSysfsPath(self) + + def getDMNode(self): + DMDevice.getDMNode(self) class MultipathDevice(DMDevice): diff --git a/storage/devicetree.py b/storage/devicetree.py index 3494d5a1d..a83279df5 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -21,6 +21,7 @@ # import os +import block from errors import * from devices import * @@ -458,7 +459,7 @@ class DeviceTree(object): raise ValueError("Cannot remove non-leaf device '%s'" % dev.name) # if this is a partition we need to remove it from the parted.Disk - if dev.type == "partition": + if isinstance(dev, PartitionDevice): dev.disk.removePartition(dev) self._devices.remove(dev) @@ -558,9 +559,9 @@ class DeviceTree(object): # special handling for extended partitions since the logical # partitions and their deps effectively depend on the extended logicals = [] - if dep.type == "partition" and dep.isExtended: + if isinstance(dep, PartitionDevice): # collect all of the logicals on the same disk - for part in self.getDevicesByType("partition"): + for part in self.getDevicesByInstance(PartitionDevice): if part.isLogical and part.disk == dep.disk: logicals.append(part) @@ -601,6 +602,13 @@ class DeviceTree(object): # this is a partition on a disk in the ignore list return True + # Ignore partitions found on the raw disks which are part of a + # dmraidset + for set in self.getDevicesByType("dm-raid array"): + for disk in set.parents: + if disk.name == os.path.basename(os.path.dirname(sysfs_path)): + return True + # FIXME: check for virtual devices whose slaves are on the ignore list def addUdevDevice(self, info): @@ -667,6 +675,19 @@ class DeviceTree(object): log.error("failure scanning device %s" % name) return + if device is None and \ + udev_device_is_dmraid_partition(info, self): + diskname = udev_device_get_dmraid_partition_disk(info) + disk = self.getDeviceByName(diskname) + device = PartitionDeviceFactory(name, \ + sysfsPath=sysfs_path, \ + major=udev_device_get_major(info), \ + minor=udev_device_get_minor(info), \ + exists=True, \ + parents=[disk]) + self._addDevice(device) + #self.ignoredDisks.append(name) + # if we get here, we found all of the slave devices and # something must be wrong -- if all of the slaves are in # the tree, this device should be as well @@ -741,6 +762,17 @@ class DeviceTree(object): minor=udev_device_get_minor(info), sysfsPath=sysfs_path) self._addDevice(device) + elif udev_device_is_dmraid(info): + # This is just temporary as I need to differentiate between the + # device that has partitions and device that dont. + log.debug("%s is part of a dmraid" % name) + device = self.getDeviceByName(name) + if device is None: + device = StorageDevice(name, + major=udev_device_get_major(info), + minor=udev_device_get_minor(info), + sysfsPath=sysfs_path, exists=True) + self._addDevice(device) elif udev_device_is_disk(info): log.debug("%s is a disk" % name) device = self.getDeviceByName(name) @@ -793,12 +825,12 @@ class DeviceTree(object): log.error("failure scanning device %s" % disk_name) return - device = PartitionDevice(name, - sysfsPath=sysfs_path, - major=udev_device_get_major(info), - minor=udev_device_get_minor(info), - exists=True, - parents=[disk]) + device = PartitionDeviceFactory(name, + sysfsPath=sysfs_path, + major=udev_device_get_major(info), + minor=udev_device_get_minor(info), + exists=True, + parents=[disk]) self._addDevice(device) # @@ -824,9 +856,8 @@ class DeviceTree(object): except KeyError: log.debug("mdraid member %s has no md uuid" % name) elif format_type == "isw_raid_member": - # dmraid - # TODO: collect name of containing raidset - # TODO: implement dmraid member format class + # We dont add any new args because we intend to use the same + # block.RaidSet object for all the related devices. pass elif format_type == "LVM2_member": # lvm @@ -914,8 +945,38 @@ class DeviceTree(object): parents=[device]) self._addDevice(md_array) elif format.type == "dmraidmember": - # look up or create the dmraid array - pass + major = udev_device_get_major(info) + minor = udev_device_get_minor(info) + # Have we already created the DMRaidArrayDevice? + rs = block.getRaidSetFromRelatedMem(uuid=uuid, name=name, + major=major, minor=minor) + if rs is None: + # FIXME: Should handle not finding a dmriad dev better + pass + + dm_array = self.getDeviceByName(rs.name) + if dm_array is not None: + # We add the new device. + dm_array._addDevice(device) + else: + # Activate the Raid set. + rs.activate(mknod=True) + + # Create the DMRaidArray + dm_array = DMRaidArrayDevice(rs.name, + major=major, minor=minor, + raidSet=rs, + level=rs.level, + parents=[device]) + + self._addDevice(dm_array) + + # Use the rs's object on the device. + # pyblock can return the memebers of a set and the device has + # the attribute to hold it. But ATM we are not really using it. + # Commenting this out until we really need it. + #device.format.raidmem = block.getMemFromRaidSet(dm_array, + # major=major, minor=minor, uuid=uuid, name=name) elif format.type == "lvmpv": # lookup/create the VG and LVs try: @@ -1077,6 +1138,9 @@ class DeviceTree(object): # TODO: expand this to catch device format types return [d for d in self._devices if d.type == device_type] + def getDevicesByInstance(self, device_class): + return [d for d in self._devices if isinstance(d, device_class)] + @property def devices(self): """ Dict with device path keys and Device values. """ diff --git a/storage/formats/dmraid.py b/storage/formats/dmraid.py index d458b45c3..ef80902e2 100644 --- a/storage/formats/dmraid.py +++ b/storage/formats/dmraid.py @@ -20,6 +20,8 @@ # Red Hat Author(s): Dave Lehman # +import block + from iutil import log_method_call #from dm import dm_node_from_name from ..errors import * @@ -65,25 +67,34 @@ class DMRaidMember(DeviceFormat): device -- path to the underlying device uuid -- this format's UUID - raidSet -- the name of the raidset this member belongs to exists -- indicates whether this is an existing format + On initialization this format is like DeviceFormat + """ log_method_call(self, *args, **kwargs) DeviceFormat.__init__(self, *args, **kwargs) - - self.raidSet = kwargs.get("raidSet") + + # Initialize the attribute that will hold the block object. + self._raidmem = None + + @property + def raidmem(self): + return self._raidmem + + @raidmem.setter + def raidmem(self, raidmem): + self._raidmem = raidmem def create(self, *args, **kwargs): log_method_call(self, device=self.device, type=self.type, status=self.status) - raise DMRaidMemberError("creation of dmraid members is not supported") + raise DMRaidMemberError("creation of dmraid members is non-sense") def destroy(self, *args, **kwargs): log_method_call(self, device=self.device, type=self.type, status=self.status) - raise DMRaidMemberError("destruction of dmraid members is not supported") - + raise DMRaidMemberError("destruction of dmraid members is non-sense") register_device_format(DMRaidMember) diff --git a/storage/partitioning.py b/storage/partitioning.py index d1f2d4d9c..78929cf46 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -32,7 +32,7 @@ from constants import * from errors import * from deviceaction import * -from devices import PartitionDevice, LUKSDevice +from devices import PartitionDeviceFactory, LUKSDevice import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -530,7 +530,7 @@ def doPartitioning(storage, exclusiveDisks=None): # that does not exist means leaving self.parents empty and instead # populating self.req_disks. In this case, we need to skip past # that since this partition is already defined. - device = PartitionDevice(extended.getDeviceNodeName(), + device = PartitionDeviceFactory(extended.getDeviceNodeName(), parents=disk) device.parents = [disk] device.partedPartition = extended diff --git a/storage/udev.py b/storage/udev.py index 6df00c855..a39b98c81 100644 --- a/storage/udev.py +++ b/storage/udev.py @@ -270,4 +270,37 @@ def udev_device_get_lv_sizes(info): return [float(s) / 1024 for s in sizes] +def udev_device_is_dmraid(info): + # Note that this function does *not* identify raid sets. + # Tests to see if device is parto of a dmraid set. + # dmraid and mdriad have the same ID_FS_USAGE string, ID_FS_TYPE has a + # string that describes the type of dmraid (isw_raid_member...), I don't + # want to maintain a list and mdraid's ID_FS_TYPE='linux_raid_member', so + # dmraid will be everything that is raid and not linux_raid_member + if info.has_key("ID_FS_USAGE") and info.has_key("ID_FS_TYPE") and \ + info["ID_FS_USAGE"] == "raid" and \ + info["ID_FS_TYPE"] != "linux_raid_member": + return True + + return False + +def udev_device_get_dmraid_partition_disk(info): + try: + p_index = info["DM_NAME"].rindex("p") + except: + return None + + if not info["DM_NAME"][p_index+1:].isdigit(): + return None + + return info["DM_NAME"][:p_index] + +def udev_device_is_dmraid_partition(info, devicetree): + diskname = udev_device_get_dmraid_partition_disk(info) + dmraid_devices = devicetree.getDevicesByType("dm-raid array") + + for device in dmraid_devices: + if diskname == device.name: + return True + return False -- cgit From 4d0b385603d192edf86039b42ae7a9ef25620f7e Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Fri, 6 Mar 2009 16:44:24 +0100 Subject: Implement the format disk question as a callback. --- storage/devices.py | 27 +++++++------ storage/devicetree.py | 107 ++++++++++++++++++++++++++++---------------------- storage/errors.py | 3 ++ 3 files changed, 78 insertions(+), 59 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index dd5a4f19d..24153fcc1 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -101,6 +101,7 @@ from devicelibs import lvm #import block from devicelibs import dm import parted +import platform from errors import * from iutil import log_method_call, notify_kernel, numeric_type @@ -636,8 +637,8 @@ class DiskDevice(StorageDevice): _type = "disk" def __init__(self, device, format=None, - size=None, major=None, minor=None, - sysfsPath='', parents=None, init = False, labeltype = None): + size=None, major=None, minor=None, sysfsPath='', \ + parents=None, initcb=None): """ Create a DiskDevice instance. Arguments: @@ -654,8 +655,7 @@ class DiskDevice(StorageDevice): parents -- a list of required Device instances removable -- whether or not this is a removable device - init -- initialize label on this drive - labeltype -- type of the label to use during initialization + initcb -- the call back to be used when initiating disk. DiskDevices always exist. @@ -671,12 +671,15 @@ class DiskDevice(StorageDevice): if not self.partedDevice: raise DeviceError("cannot find parted device instance") log.debug("creating parted Disk: %s" % self.path) - if init: - self.partedDisk = parted.freshDisk(device=self.partedDevice, ty = labeltype) - else: + try: self.partedDisk = parted.Disk(device=self.partedDevice) - if not self.partedDisk: - raise DeviceError("failed to create parted Disk") + except parted.IOException: + # if we have a cb function use it. else an error. + if initcb is not None and initcb(): + self.partedDisk = parted.freshDisk(device=self.partedDevice, \ + ty = platform.getPlatform(None).diskType) + else: + raise DeviceUserDeniedFormatError("User prefered to not format.") self.probe() @@ -2159,7 +2162,7 @@ class DMRaidArrayDevice(DiskDevice): devDir = "/dev/mapper" def __init__(self, name, raidSet=None, level=None, format=None, size=None, - major=None, minor=None, parents=None, sysfsPath=''): + major=None, minor=None, parents=None, sysfsPath='', initcb=None): """ Create a DMRaidArrayDevice instance. Arguments: @@ -2180,8 +2183,8 @@ class DMRaidArrayDevice(DiskDevice): if not parent.format or parent.format.type != "dmraidmember": raise ValueError("parent devices must contain dmraidmember format") DiskDevice.__init__(self, name, format=format, size=size, - major=major, minor=minor, - parents=parents, sysfsPath=sysfsPath) + major=major, minor=minor, parents=parents, + sysfsPath=sysfsPath, initcb=initcb) self.formatClass = get_device_format_class("dmraidmember") if not self.formatClass: diff --git a/storage/devicetree.py b/storage/devicetree.py index a83279df5..09a5bf4f8 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -28,8 +28,6 @@ from devices import * from deviceaction import * import formats from udev import * -import parted -import platform import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -117,7 +115,26 @@ def getLUKSPassphrase(intf, device, globalPassphrase): return (passphrase, isglobal) - +# Don't really know where to put this. +def questionInitializeDisk(intf=None, name=None): + retVal = False # The less destructive default + if not intf or not name: + pass + else: + rc = intf.messageWindow(_("Warning"), + _("Error processing drive %s.\n" + "Maybe it needs to be reinitialized." + "YOU WILL LOSE ALL DATA ON THIS DRIVE!") % (name,), + type="custom", + custom_buttons = [ _("_Ignore drive"), + _("_Re-initialize drive") ], + custom_icon="question") + if rc == 0: + pass + else: + retVal = True + return retVal + class DeviceTree(object): """ A quasi-tree that represents the devices in the system. @@ -778,32 +795,15 @@ class DeviceTree(object): device = self.getDeviceByName(name) if device is None: try: + cb=lambda:questionInitializeDisk(self.intf, name) device = DiskDevice(name, major=udev_device_get_major(info), minor=udev_device_get_minor(info), - sysfsPath=sysfs_path) + sysfsPath=sysfs_path, + initcb=cb) self._addDevice(device) - except parted.IOException: #drive not initialized? - if not self.intf: - self.ignoredDisks.append(name) - else: - rc = self.intf.messageWindow(_("Warning"), - _("Error processing drive %s.\n" - "Maybe it needs to be reinitialized." - "YOU WILL LOSE ALL DATA ON THIS DRIVE!") % (name,), - type="custom", - custom_buttons = [ _("_Ignore drive"), - _("_Re-initialize drive") ], - custom_icon="question") - if rc == 0: - self.ignoredDisks.append(name) - else: - device = DiskDevice(name, - major=udev_device_get_major(info), - minor=udev_device_get_minor(info), - sysfsPath=sysfs_path, init = True, - labeltype = platform.getPlatform(None).diskType) - self._addDevice(device) + except DeviceUserDeniedFormatError: #drive not initialized? + self.ignoredDisks.append(name) elif udev_device_is_partition(info): log.debug("%s is a partition" % name) device = self.getDeviceByName(name) @@ -954,29 +954,42 @@ class DeviceTree(object): # FIXME: Should handle not finding a dmriad dev better pass - dm_array = self.getDeviceByName(rs.name) - if dm_array is not None: - # We add the new device. - dm_array._addDevice(device) - else: - # Activate the Raid set. - rs.activate(mknod=True) - - # Create the DMRaidArray - dm_array = DMRaidArrayDevice(rs.name, - major=major, minor=minor, - raidSet=rs, - level=rs.level, - parents=[device]) - - self._addDevice(dm_array) + if rs.name in self.ignoredDisks: + # If the rs is being ignored, we should ignore device too. + self.ignoredDisks.append(device.name) - # Use the rs's object on the device. - # pyblock can return the memebers of a set and the device has - # the attribute to hold it. But ATM we are not really using it. - # Commenting this out until we really need it. - #device.format.raidmem = block.getMemFromRaidSet(dm_array, - # major=major, minor=minor, uuid=uuid, name=name) + else: + dm_array = self.getDeviceByName(rs.name) + if dm_array is not None: + # We add the new device. + dm_array._addDevice(device) + else: + # Activate the Raid set. + rs.activate(mknod=True) + + # Create the DMRaidArray + try: + cb=lambda:questionInitializeDisk(self.intf,rs.name) + dm_array = DMRaidArrayDevice(rs.name, + major=major, minor=minor, + raidSet=rs, + level=rs.level, + parents=[device], + initcb=cb) + + self._addDevice(dm_array) + # Use the rs's object on the device. + # pyblock can return the memebers of a set and the + # device has the attribute to hold it. But ATM we + # are not really using it. Commenting this out until + # we really need it. + #device.format.raidmem = block.getMemFromRaidSet(dm_array, + # major=major, minor=minor, uuid=uuid, name=name) + except DeviceUserDeniedFormatError: + # We should ignore the dmriad and its components + self.ignoredDisks.append(rs.name) + self.ignoredDisks.append(device.name) + rs.deactivate() elif format.type == "lvmpv": # lookup/create the VG and LVs try: diff --git a/storage/errors.py b/storage/errors.py index 692e0acc9..8fb57b140 100644 --- a/storage/errors.py +++ b/storage/errors.py @@ -21,6 +21,9 @@ class DeviceTeardownError(DeviceError): class DeviceResizeError(DeviceError): pass +class DeviceUserDeniedFormatError(DeviceError): + pass + # DeviceFormat class DeviceFormatError(Exception): pass -- cgit From f15832a4220d89c3182ea25aeb71b9ed5a44c067 Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Fri, 6 Mar 2009 18:43:05 +0100 Subject: various dmraid fixes. 1. Make a better job at detecting a dmraid member. 2. Have python raise an error when trying to set the raidSet 3. For now ignore the raid member if we can't find the set. --- storage/devices.py | 6 ------ storage/devicetree.py | 9 ++++++--- storage/udev.py | 6 +++--- 3 files changed, 9 insertions(+), 12 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 24153fcc1..a61249a70 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -2198,12 +2198,6 @@ class DMRaidArrayDevice(DiskDevice): def raidSet(self): return self._raidSet - @raidSet.setter - def raidSet(self, val): - # If we change self._raidSet, parents list will be invalid. - # Don't allow the change. - pass - def _addDevice(self, device): """ Add a new member device to the array. diff --git a/storage/devicetree.py b/storage/devicetree.py index 09a5bf4f8..2a9d62ea2 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -951,12 +951,15 @@ class DeviceTree(object): rs = block.getRaidSetFromRelatedMem(uuid=uuid, name=name, major=major, minor=minor) if rs is None: - # FIXME: Should handle not finding a dmriad dev better - pass + # we ignore the device in the hope that all the devices + # from this set will be ignored. + self.ignoredDisks.append(device.name) + return - if rs.name in self.ignoredDisks: + elif rs.name in self.ignoredDisks: # If the rs is being ignored, we should ignore device too. self.ignoredDisks.append(device.name) + return else: dm_array = self.getDeviceByName(rs.name) diff --git a/storage/udev.py b/storage/udev.py index a39b98c81..1263823ab 100644 --- a/storage/udev.py +++ b/storage/udev.py @@ -277,9 +277,9 @@ def udev_device_is_dmraid(info): # string that describes the type of dmraid (isw_raid_member...), I don't # want to maintain a list and mdraid's ID_FS_TYPE='linux_raid_member', so # dmraid will be everything that is raid and not linux_raid_member - if info.has_key("ID_FS_USAGE") and info.has_key("ID_FS_TYPE") and \ - info["ID_FS_USAGE"] == "raid" and \ - info["ID_FS_TYPE"] != "linux_raid_member": + from formats.dmraid import DMRaidMember + if info.has_key("ID_FS_TYPE") and \ + info["ID_FS_TYPE"] in DMRaidMember._udevTypes: return True return False -- cgit From 1fd5cc78a4d0f2094d760fb7284ec8d778a6c0ff Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 6 Mar 2009 12:07:00 -0600 Subject: Catch all failures from making parted objects in exceptionDisks. --- storage/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 6c241c2c0..8dda2a421 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -27,7 +27,6 @@ import errno import sys import parted -import _ped import isys import iutil @@ -397,7 +396,7 @@ class Storage(object): try: dev = parted.Device(path=device.path) disk = parted.Disk(device=dev) - except (parted.DeviceException, _ped.DeviceException): + except Exception: continue for part in disk.partitions: -- cgit From daf87c6711ef1f866eeeeb4c0804ece67b55d563 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 6 Mar 2009 12:09:13 -0600 Subject: Catch the new _ped.DiskLabelException for unrecognized disklabels. --- storage/devices.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/storage/devices.py b/storage/devices.py index a61249a70..8dc20c132 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -101,6 +101,7 @@ from devicelibs import lvm #import block from devicelibs import dm import parted +import _ped import platform from errors import * @@ -673,7 +674,7 @@ class DiskDevice(StorageDevice): log.debug("creating parted Disk: %s" % self.path) try: self.partedDisk = parted.Disk(device=self.partedDevice) - except parted.IOException: + except _ped.DiskLabelException: # if we have a cb function use it. else an error. if initcb is not None and initcb(): self.partedDisk = parted.freshDisk(device=self.partedDevice, \ -- cgit From a17c0acc8d4960f035cb3e450d0986513d5b8d7d Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Thu, 5 Mar 2009 12:18:32 +0100 Subject: Use correct storage attribute for ks clearpart --- kickstart.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kickstart.py b/kickstart.py index 8a6b743ca..8733e6b14 100644 --- a/kickstart.py +++ b/kickstart.py @@ -237,8 +237,8 @@ class ClearPart(commands.clearpart.FC3_ClearPart): if disk not in hds: raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in clearpart command" % disk) - self.handler.id.storage.autoClearPartType = self.type - self.handler.id.storage.autoClearPartDrives = self.drives + self.handler.id.storage.clearPartType = self.type + self.handler.id.storage.clearPartDisks = self.drives if self.initAll: self.handler.id.storage.reinitializeDisks = self.initAll -- cgit From e7e0ad9239cb757ce18e07a8717dd06a9f032507 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Thu, 5 Mar 2009 12:19:09 +0100 Subject: Update storage flag on ks autopart --- kickstart.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kickstart.py b/kickstart.py index 8733e6b14..996a1fc7f 100644 --- a/kickstart.py +++ b/kickstart.py @@ -142,6 +142,7 @@ class AutoPart(commands.autopart.F9_AutoPart): # sets up default autopartitioning. use clearpart separately # if you want it self.handler.id.instClass.setDefaultPartitioning(self.handler.id.storage, self.handler.anaconda.platform, doClear = 0) + self.handler.id.storage.doAutoPart = True if self.encrypted: self.handler.id.storage.autoEncrypt = True -- cgit From 8f710f2c773a4af3a4fc652576dbda0db7380b44 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 6 Mar 2009 11:26:55 -0500 Subject: Make sure autopart without any clearpart command will fail. The crucial step here is making sure we set clearPartType to NONE in setDefaultPartitioning when called with doClear=False. That's what we pass in from the autopart kickstart command handler, and that's what forces you to have to use clearpart if you want to remove existing partitions. --- installclass.py | 2 ++ kickstart.py | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/installclass.py b/installclass.py index 8e43df193..bca365e32 100644 --- a/installclass.py +++ b/installclass.py @@ -200,6 +200,8 @@ class BaseInstallClass(object): if doClear: storage.clearPartType = clear storage.clearPartDisks = [] + else: + storage.clearPartType = CLEARPART_TYPE_NONE storage.autoPartitionRequests = autorequests diff --git a/kickstart.py b/kickstart.py index 996a1fc7f..012d1bc73 100644 --- a/kickstart.py +++ b/kickstart.py @@ -141,11 +141,11 @@ class AutoPart(commands.autopart.F9_AutoPart): # sets up default autopartitioning. use clearpart separately # if you want it - self.handler.id.instClass.setDefaultPartitioning(self.handler.id.storage, self.handler.anaconda.platform, doClear = 0) + self.handler.id.instClass.setDefaultPartitioning(self.handler.id.storage, self.handler.anaconda.platform, doClear=False) self.handler.id.storage.doAutoPart = True if self.encrypted: - self.handler.id.storage.autoEncrypt = True + self.handler.id.storage.encryptedAutoPart = True self.handler.id.storage.encryptionPassphrase = self.passphrase self.handler.skipSteps.extend(["partition", "zfcpconfig", "parttype"]) -- cgit From 6c41fc2205163d2106e79f5a0c32afe51e93bfb8 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 6 Mar 2009 12:51:14 -0500 Subject: Start storage before parsing the kickstart file. This is needed to perform sanity checks on the values given to things like clearpart --drives, as well as to add requests later on. Waiting until we're into the dispatcher steps is too late. --- kickstart.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kickstart.py b/kickstart.py index 012d1bc73..9e95fdbde 100644 --- a/kickstart.py +++ b/kickstart.py @@ -1008,9 +1008,10 @@ def addPartRequest(anaconda, request): anaconda.id.storage.autoPartitionRequests.append(request) def processKickstartFile(anaconda, file): - # make sure our disks are alive - from partedUtils import DiskSet - ds = DiskSet(anaconda) + # We need to make sure storage is active before the kickstart file is read. + import storage + storage.storageInitialize(anaconda) + anaconda.dispatch.skipStep("storageinit") # parse the %pre ksparser = KickstartPreParser(AnacondaKSHandler(anaconda)) -- cgit From 99095913b9b47059e3c3e1706d49176716a490f2 Mon Sep 17 00:00:00 2001 From: Martin Gracik Date: Thu, 5 Mar 2009 12:28:37 +0100 Subject: Added crypto.py unittest; Updated devicelibs tests baseclass.py and lvm.py baseclass.py needed some imports to work properly. Updated the test for getMaxLVSize() in lvm.py to not pass PE size as argument. --- tests/storage/devicelibs/baseclass.py | 4 ++ tests/storage/devicelibs/crypto.py | 123 ++++++++++++++++++++++++++++++++++ tests/storage/devicelibs/lvm.py | 3 +- 3 files changed, 128 insertions(+), 2 deletions(-) create mode 100644 tests/storage/devicelibs/crypto.py diff --git a/tests/storage/devicelibs/baseclass.py b/tests/storage/devicelibs/baseclass.py index d1264f5b6..efc9c803f 100644 --- a/tests/storage/devicelibs/baseclass.py +++ b/tests/storage/devicelibs/baseclass.py @@ -2,6 +2,10 @@ import unittest import os import subprocess +# need to import these first before doing anything +import anaconda_log +import upgrade + class TestDevicelibs(unittest.TestCase): _LOOP_DEVICES = (("/dev/loop0", "/tmp/test-virtdev0"), diff --git a/tests/storage/devicelibs/crypto.py b/tests/storage/devicelibs/crypto.py new file mode 100644 index 000000000..6a76569c5 --- /dev/null +++ b/tests/storage/devicelibs/crypto.py @@ -0,0 +1,123 @@ +import baseclass +import unittest +import storage.devicelibs.crypto as crypto + +import tempfile +import os + +class TestCrypto(baseclass.TestDevicelibs): + + def runTest(self): + ## + ## is_luks + ## + # pass + self.assertEqual(crypto.is_luks(self._LOOP_DEV0), -22) + self.assertEqual(crypto.is_luks("/not/existing/device"), -22) + + ## + ## luks_format + ## + # pass + self.assertEqual(crypto.luks_format(self._LOOP_DEV0, passphrase="secret", cipher="aes-cbc-essiv:sha256", key_size=256), None) + + # make a key file + handle, keyfile = tempfile.mkstemp(prefix="key", text=False) + os.write(handle, "nobodyknows") + os.close(handle) + + # format with key file + self.assertEqual(crypto.luks_format(self._LOOP_DEV1, key_file=keyfile), None) + + # fail + self.assertRaises(crypto.CryptoError, crypto.luks_format, "/not/existing/device", passphrase="secret", cipher="aes-cbc-essiv:sha256", key_size=256) + # no passhprase or key file + self.assertRaises(ValueError, crypto.luks_format, self._LOOP_DEV1, cipher="aes-cbc-essiv:sha256", key_size=256) + + ## + ## is_luks + ## + # pass + self.assertEqual(crypto.is_luks(self._LOOP_DEV0), 0) # 0 = is luks + self.assertEqual(crypto.is_luks(self._LOOP_DEV1), 0) + + ## + ## luks_add_key + ## + # pass + self.assertEqual(crypto.luks_add_key(self._LOOP_DEV0, new_passphrase="another-secret", passphrase="secret"), None) + + # make another key file + handle, new_keyfile = tempfile.mkstemp(prefix="key", text=False) + os.write(handle, "area51") + os.close(handle) + + # add new key file + self.assertEqual(crypto.luks_add_key(self._LOOP_DEV1, new_key_file=new_keyfile, key_file=keyfile), None) + + # fail + self.assertRaises(RuntimeError, crypto.luks_add_key, self._LOOP_DEV0, new_passphrase="another-secret", passphrase="wrong-passphrase") + + ## + ## luks_remove_key + ## + # fail + self.assertRaises(RuntimeError, crypto.luks_remove_key, self._LOOP_DEV0, del_passphrase="another-secret", passphrase="wrong-pasphrase") + + # pass + self.assertEqual(crypto.luks_remove_key(self._LOOP_DEV0, del_passphrase="another-secret", passphrase="secret"), None) + + # remove key file + self.assertEqual(crypto.luks_remove_key(self._LOOP_DEV1, del_key_file=new_keyfile, key_file=keyfile), None) + + ## + ## luks_open + ## + # pass + self.assertEqual(crypto.luks_open(self._LOOP_DEV0, "crypted", passphrase="secret"), None) + self.assertEqual(crypto.luks_open(self._LOOP_DEV1, "encrypted", key_file=keyfile), None) + + # fail + self.assertRaises(crypto.CryptoError, crypto.luks_open, "/not/existing/device", "another-crypted", passphrase="secret") + self.assertRaises(crypto.CryptoError, crypto.luks_open, "/not/existing/device", "another-crypted", key_file=keyfile) + # no passhprase or key file + self.assertRaises(ValueError, crypto.luks_open, self._LOOP_DEV1, "another-crypted") + + ## + ## luks_status + ## + # pass + self.assertEqual(crypto.luks_status("crypted"), True) + self.assertEqual(crypto.luks_status("encrypted"), True) + self.assertEqual(crypto.luks_status("another-crypted"), False) + + ## + ## luks_uuid + ## + # pass + uuid = crypto.luks_uuid(self._LOOP_DEV0) + self.assertEqual(crypto.luks_uuid(self._LOOP_DEV0), uuid) + uuid = crypto.luks_uuid(self._LOOP_DEV1) + self.assertEqual(crypto.luks_uuid(self._LOOP_DEV1), uuid) + + ## + ## luks_close + ## + # pass + self.assertEqual(crypto.luks_close("crypted"), None) + self.assertEqual(crypto.luks_close("encrypted"), None) + + # fail + self.assertRaises(crypto.CryptoError, crypto.luks_close, "wrong-name") + # already closed + self.assertRaises(crypto.CryptoError, crypto.luks_close, "crypted") + self.assertRaises(crypto.CryptoError, crypto.luks_close, "encrypted") + + # cleanup + os.unlink(keyfile) + os.unlink(new_keyfile) + + +if __name__ == "__main__": + suite = unittest.TestLoader().loadTestsFromTestCase(TestCrypto) + unittest.TextTestRunner(verbosity=2).run(suite) diff --git a/tests/storage/devicelibs/lvm.py b/tests/storage/devicelibs/lvm.py index 210ad9d95..62c52d168 100644 --- a/tests/storage/devicelibs/lvm.py +++ b/tests/storage/devicelibs/lvm.py @@ -202,8 +202,7 @@ class TestLVM(baseclass.TestDevicelibs): #def testGetMaxLVSize(self): # pass - # why do we specify the PE ? not needed... - self.assertEqual(lvm.getMaxLVSize(4), 16*1024**2) + self.assertEqual(lvm.getMaxLVSize(), 16*1024**2) #def testSafeLVMName(self): # pass -- cgit From e60e58b1f7a659b448f4080075b399538878c501 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 9 Mar 2009 10:34:15 -0400 Subject: It's self.origrequest, not self.origreqest (#489036). --- iw/raid_dialog_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index 0b9d29573..efca453a7 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -250,7 +250,7 @@ class RaidEditor: actions.append(ActionCreateDevice(request)) actions.append(ActionCreateFormat(request)) elif format: - actions.append(ActionCreateFormat(self.origreqest, format)) + actions.append(ActionCreateFormat(self.origrequest, format)) if luksdev: -- cgit From 9b23fb1cd22f6f2cc4f6b18897e65ab1e4d68328 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 9 Mar 2009 13:19:55 +0100 Subject: Fix booty for dmraid Booty was expecting the xxxxxxx part of /dev/xxxxxx paths instead of new storage device names, this patch fixes this, making booty work on dmraid with the new storage code. --- bootloader.py | 4 ++-- booty/alpha.py | 4 ++-- booty/bootloaderInfo.py | 2 +- booty/checkbootloader.py | 18 +++++++------- booty/ppc.py | 2 +- booty/sparc.py | 2 +- booty/util.py | 9 +++---- booty/x86.py | 51 ++++++++++++++++++++++------------------ iw/upgrade_bootloader_gui.py | 4 ++-- textw/upgrade_bootloader_text.py | 4 ++-- 10 files changed, 53 insertions(+), 47 deletions(-) diff --git a/bootloader.py b/bootloader.py index 313ecdff7..f9209528c 100644 --- a/bootloader.py +++ b/bootloader.py @@ -121,12 +121,12 @@ def writeBootloader(anaconda): # now make the upgrade stuff work for kickstart too. ick. if anaconda.isKickstart and anaconda.id.bootloader.doUpgradeOnly: import checkbootloader - (bootType, theDev) = checkbootloader.getBootloaderTypeAndBoot(anaconda.rootPath) + (bootType, theDev) = checkbootloader.getBootloaderTypeAndBoot(anaconda.rootPath, storage=anaconda.id.storage) anaconda.id.bootloader.doUpgradeonly = 1 if bootType == "GRUB": anaconda.id.bootloader.useGrubVal = 1 - anaconda.id.bootloader.setDevice(theDev) + anaconda.id.bootloader.setDevice(theDev.split("/")[-1]) else: anaconda.id.bootloader.doUpgradeOnly = 0 diff --git a/booty/alpha.py b/booty/alpha.py index abd3a636a..ca715560d 100644 --- a/booty/alpha.py +++ b/booty/alpha.py @@ -7,13 +7,13 @@ from util import getDiskPart class alphaBootloaderInfo(bootloaderInfo): def wholeDevice (self, path): - (device, foo) = getDiskPart(path) + (device, foo) = getDiskPart(path, self.storage) return device def partitionNum (self, path): # getDiskPart returns part numbers 0-based; we need it one based # *sigh* - (foo, partitionNumber) = getDiskPart(path) + (foo, partitionNumber) = getDiskPart(path, self.storage) return partitionNumber + 1 def writeAboot(self, instRoot, bl, kernelList, diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index f9f684b95..2fdf16ab7 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -295,7 +295,7 @@ class bootloaderInfo: def setDevice(self, device): self.device = device - (dev, part) = getDiskPart(device) + (dev, part) = getDiskPart(device, self.storage) if part is None: self.defaultDevice = "mbr" else: diff --git a/booty/checkbootloader.py b/booty/checkbootloader.py index 7c6241676..9508e14a6 100644 --- a/booty/checkbootloader.py +++ b/booty/checkbootloader.py @@ -27,7 +27,7 @@ liloConfigFile = "/etc/lilo.conf" yabootConfigFile = "/etc/yaboot.conf" siloConfigFile = "/etc/silo.conf" -def getRaidDisks(raidDevice, raidLevel=None, stripPart=1): +def getRaidDisks(raidDevice, storage, raidLevel=None, stripPart=1): rc = [] if raidLevel is not None: try: @@ -54,7 +54,7 @@ def getRaidDisks(raidDevice, raidLevel=None, stripPart=1): if len(dev) == 0: continue if stripPart: - disk = getDiskPart(dev)[0] + disk = getDiskPart(dev, storage)[0] rc.append(disk) else: rc.append(dev) @@ -62,7 +62,7 @@ def getRaidDisks(raidDevice, raidLevel=None, stripPart=1): return rc -def getBootBlock(bootDev, instRoot, seekBlocks=0): +def getBootBlock(bootDev, instRoot, storage, seekBlocks=0): """Get the boot block from bootDev. Return a 512 byte string.""" block = " " * 512 if bootDev is None: @@ -70,7 +70,7 @@ def getBootBlock(bootDev, instRoot, seekBlocks=0): # get the devices in the raid device if bootDev[5:7] == "md": - bootDevs = getRaidDisks(bootDev[5:]) + bootDevs = getRaidDisks(bootDev[5:], storage) bootDevs.sort() else: bootDevs = [ bootDev[5:] ] @@ -108,7 +108,7 @@ def getBootDevList(line): rets.append(dev) return string.join(rets) -def getBootloaderTypeAndBoot(instRoot = "/"): +def getBootloaderTypeAndBoot(instRoot, storage): haveGrubConf = 1 haveLiloConf = 1 haveYabootConf = 1 @@ -150,7 +150,7 @@ def getBootloaderTypeAndBoot(instRoot = "/"): return ("GRUB", bootDev) if bootDev is not None: - block = getBootBlock(bootDev, instRoot) + block = getBootBlock(bootDev, instRoot, storage) # XXX I don't like this, but it's what the maintainer suggested :( if string.find(block, "GRUB") >= 0: return ("GRUB", bootDev) @@ -163,7 +163,7 @@ def getBootloaderTypeAndBoot(instRoot = "/"): bootDev = getBootDevString(line) break - block = getBootBlock(bootDev, instRoot) + block = getBootBlock(bootDev, instRoot, storage) # this at least is well-defined if block[6:10] == "LILO": return ("LILO", bootDev) @@ -200,8 +200,8 @@ def getBootloaderTypeAndBoot(instRoot = "/"): if bootDev is not None: # XXX SILO sucks just like grub. - if getDiskPart(bootDev)[1] != 3: - block = getBootBlock(bootDev, instRoot, 1) + if getDiskPart(bootDev, storage)[1] != 3: + block = getBootBlock(bootDev, instRoot, storage, 1) if block[24:28] == "SILO": return ("SILO", bootDev) diff --git a/booty/ppc.py b/booty/ppc.py index a68c5938d..7ac9299a3 100644 --- a/booty/ppc.py +++ b/booty/ppc.py @@ -64,7 +64,7 @@ class ppcBootloaderInfo(bootloaderInfo): f.write("init-message=\"Welcome to %s!\\nHit for boot options\"\n\n" % productName) - (name, partNum) = getDiskPart(bootDev) + (name, partNum) = getDiskPart(bootDev, self.storage) partno = partNum + 1 # 1 based f.write("partition=%s\n" %(partno,)) diff --git a/booty/sparc.py b/booty/sparc.py index 5ea45eebe..4ea2ceaf5 100644 --- a/booty/sparc.py +++ b/booty/sparc.py @@ -35,7 +35,7 @@ class sparcBootloaderInfo(bootloaderInfo): f.write("message=%s\n" % (mf,)) f.write("timeout=%s\n" % (self.timeout or 50)) - (name, partNum) = getDiskPart(bootDev) + (name, partNum) = getDiskPart(bootDev, self.storage) partno = partNum + 1 f.write("partition=%s\n" % (partno,)) diff --git a/booty/util.py b/booty/util.py index f9a1b3e84..74ba561ed 100644 --- a/booty/util.py +++ b/booty/util.py @@ -1,10 +1,11 @@ import string -def getDiskPart(dev): +def getDiskPart(dev, storage): + path = storage.devicetree.getDeviceByName(dev).path[5:] cut = len(dev) - if (dev.startswith('rd/') or dev.startswith('ida/') or - dev.startswith('cciss/') or dev.startswith('sx8/') or - dev.startswith('mapper/') or dev.startswith('mmcblk')): + if (path.startswith('rd/') or path.startswith('ida/') or + path.startswith('cciss/') or path.startswith('sx8/') or + path.startswith('mapper/') or path.startswith('mmcblk')): if dev[-2] == 'p': cut = -1 elif dev[-3] == 'p': diff --git a/booty/x86.py b/booty/x86.py index de88458e8..05c9feb75 100644 --- a/booty/x86.py +++ b/booty/x86.py @@ -46,15 +46,18 @@ class x86BootloaderInfo(efiBootloaderInfo): # Accepted values for "device" are raid1 md devices (i.e. "md0"), # physical disks ("hda"), and real partitions on physical disks # ("hda1"). Volume groups/logical volumes are not accepted. - if string.split(device, '/', 1)[0] in map (lambda x: x.name, self.storage.lvs + self.storage.vgs): + path = self.storage.devicetree.getDeviceByName(device).path[5:] + + if device in map (lambda x: x.name, self.storage.lvs + self.storage.vgs): return [] - if device.startswith("mapper/luks-"): + if path.startswith("mapper/luks-"): return [] - if device.startswith('md'): + if path.startswith('md'): bootable = 0 - parts = checkbootloader.getRaidDisks(device, 1, stripPart=0) + parts = checkbootloader.getRaidDisks(device, self.storage, + raidLevel=1, stripPart=0) parts.sort() return parts @@ -107,7 +110,7 @@ class x86BootloaderInfo(efiBootloaderInfo): cmds = [] for bootDev in bootDevs: gtPart = self.getMatchingPart(bootDev, grubTarget) - gtDisk = self.grubbyPartitionName(getDiskPart(gtPart)[0]) + gtDisk = self.grubbyPartitionName(getDiskPart(gtPart, self.storage)[0]) bPart = self.grubbyPartitionName(bootDev) cmd = "root %s\n" % (bPart,) @@ -135,18 +138,19 @@ class x86BootloaderInfo(efiBootloaderInfo): os.rename(cf, cf + '.rpmsave') grubTarget = bl.getDevice() + path = self.storage.devicetree.getDeviceByName(grubTarget).path[5:] target = "mbr" - if (grubTarget.startswith('rd/') or grubTarget.startswith('ida/') or - grubTarget.startswith('cciss/') or - grubTarget.startswith('sx8/') or - grubTarget.startswith('mapper/')): + if (path.startswith('rd/') or path.startswith('ida/') or + path.startswith('cciss/') or + path.startswith('sx8/') or + path.startswith('mapper/')): if grubTarget[-1].isdigit(): if grubTarget[-2] == 'p' or \ (grubTarget[-2].isdigit() and grubTarget[-3] == 'p'): target = "partition" - elif grubTarget[-1].isdigit() and not grubTarget.startswith('md'): + elif grubTarget[-1].isdigit() and not path.startswith('md'): target = "partition" - + f = open(cf, "w+") f.write("# grub.conf generated by anaconda\n") @@ -316,7 +320,7 @@ class x86BootloaderInfo(efiBootloaderInfo): devs = usedDevs.keys() usedDevs = {} for dev in devs: - drive = getDiskPart(dev)[0] + drive = getDiskPart(dev, self.storage)[0] if usedDevs.has_key(drive): continue usedDevs[drive] = 1 @@ -325,9 +329,9 @@ class x86BootloaderInfo(efiBootloaderInfo): for drive in devs: # XXX hack city. If they're not the sort of thing that'll # be in the device map, they shouldn't still be in the list. + path = self.storage.devicetree.getDeviceByName(drive).path if not drive.startswith('md'): - f.write("(%s) /dev/%s\n" % (self.grubbyDiskName(drive), - drive)) + f.write("(%s) %s\n" % (self.grubbyDiskName(drive), path)) f.close() sysconf = '/etc/sysconfig/grub' @@ -356,10 +360,10 @@ class x86BootloaderInfo(efiBootloaderInfo): return "" def getMatchingPart(self, bootDev, target): - bootName, bootPartNum = getDiskPart(bootDev) + bootName, bootPartNum = getDiskPart(bootDev, self.storage) devices = self.getPhysicalDevices(target) for device in devices: - name, partNum = getDiskPart(device) + name, partNum = getDiskPart(device, self.storage) if name == bootName: return device return devices[0] @@ -368,7 +372,7 @@ class x86BootloaderInfo(efiBootloaderInfo): return "hd%d" % self.drivelist.index(name) def grubbyPartitionName(self, dev): - (name, partNum) = getDiskPart(dev) + (name, partNum) = getDiskPart(dev, self.storage) if partNum != None: return "(%s,%d)" % (self.grubbyDiskName(name), partNum) else: @@ -448,23 +452,24 @@ class x86BootloaderInfo(efiBootloaderInfo): # so we have to do shenanigans to get updated grub installed... # steal some more code above try: - bootDev = self.storage.fsset.mountpoints["/boot"] + bootDev = self.storage.fsset.mountpoints["/boot"].name grubPath = "/grub" cfPath = "/" except KeyError: - bootDev = self.storage.fsset.rootDevice + bootDev = self.storage.fsset.rootDevice.name grubPath = "/boot/grub" cfPath = "/boot/" masterBootDev = bootDev if masterBootDev[0:2] == 'md': - rootDevs = checkbootloader.getRaidDisks(masterBootDev, raidLevel=1, - stripPart = 0) + rootDevs = checkbootloader.getRaidDisks(masterBootDev, + self.storage, raidLevel=1, stripPart=0) else: rootDevs = [masterBootDev] if theDev[5:7] == 'md': - stage1Devs = checkbootloader.getRaidDisks(theDev[5:], raidLevel=1) + stage1Devs = checkbootloader.getRaidDisks(theDev[5:], self.storage, + raidLevel=1) else: stage1Devs = [theDev[5:]] @@ -478,7 +483,7 @@ class x86BootloaderInfo(efiBootloaderInfo): grubbyRootPart = self.grubbyPartitionName(rootDevs[0]) for rootDev in rootDevs: - testGrubbyRootDev = getDiskPart(rootDev)[0] + testGrubbyRootDev = getDiskPart(rootDev, self.storage)[0] testGrubbyRootDev = self.grubbyPartitionName(testGrubbyRootDev) if grubbyStage1Dev == testGrubbyRootDev: diff --git a/iw/upgrade_bootloader_gui.py b/iw/upgrade_bootloader_gui.py index 63ebb9d64..57cfc63b3 100644 --- a/iw/upgrade_bootloader_gui.py +++ b/iw/upgrade_bootloader_gui.py @@ -61,7 +61,7 @@ class UpgradeBootloaderWindow (InstallWindow): self.bl.useGrubVal = 1 else: self.bl.useGrubVal = 0 - self.bl.setDevice(self.bootDev) + self.bl.setDevice(self.bootDev.split("/")[-1]) def _newToLibata(self, rootPath): # NOTE: any changes here need to be done in upgrade_bootloader_text too @@ -117,7 +117,7 @@ class UpgradeBootloaderWindow (InstallWindow): newToLibata = self._newToLibata(anaconda.rootPath) (self.type, self.bootDev) = \ - checkbootloader.getBootloaderTypeAndBoot(anaconda.rootPath) + checkbootloader.getBootloaderTypeAndBoot(anaconda.rootPath, storage=anaconda.id.storage) self.update_radio = gtk.RadioButton(None, _("_Update boot loader configuration")) updatestr = _("This will update your current boot loader.") diff --git a/textw/upgrade_bootloader_text.py b/textw/upgrade_bootloader_text.py index 636d78998..f863efbb1 100644 --- a/textw/upgrade_bootloader_text.py +++ b/textw/upgrade_bootloader_text.py @@ -86,7 +86,7 @@ class UpgradeBootloaderWindow: newToLibata = self._ideToLibata(anaconda.rootPath) (self.type, self.bootDev) = \ - checkbootloader.getBootloaderTypeAndBoot(anaconda.rootPath) + checkbootloader.getBootloaderTypeAndBoot(anaconda.rootPath, storage=anaconda.id.storage) blradio = RadioGroup() @@ -173,7 +173,7 @@ class UpgradeBootloaderWindow: self.bl.useGrubVal = 1 else: self.bl.useGrubVal = 0 - self.bl.setDevice(self.bootDev) + self.bl.setDevice(self.bootDev.split("/")[-1]) -- cgit From b8e2d97b81fe264fc20f2337e68c9197066950de Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 9 Mar 2009 14:25:53 -0400 Subject: stdout and stderr may also need to be created. This is required when handling kickstart scriptlets that can write to log files, since the log file will need to be created before it can be opened. --- iutil.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/iutil.py b/iutil.py index a5082195c..d9e8a776a 100644 --- a/iutil.py +++ b/iutil.py @@ -65,14 +65,14 @@ def execWithRedirect(command, argv, stdin = None, stdout = None, stdin = sys.stdin.fileno() if isinstance(stdout, str): - stdout = os.open(stdout, os.O_RDWR) + stdout = os.open(stdout, os.O_RDWR|os.O_CREAT) elif isinstance(stdout, int): pass elif stdout is None or not isinstance(stdout, file): stdout = sys.stdout.fileno() if isinstance(stderr, str): - stderr = os.open(stderr, os.O_RDWR) + stderr = os.open(stderr, os.O_RDWR|os.O_CREAT) elif isinstance(stderr, int): pass elif stderr is None or not isinstance(stderr, file): @@ -135,7 +135,7 @@ def execWithCapture(command, argv, stdin = None, stderr = None, root='/'): stdin = sys.stdin.fileno() if isinstance(stderr, str): - stderr = os.open(stderr, os.O_RDWR) + stderr = os.open(stderr, os.O_RDWR|os.O_CREAT) elif isinstance(stderr, int): pass elif stderr is None or not isinstance(stderr, file): @@ -184,14 +184,14 @@ def execWithPulseProgress(command, argv, stdin = None, stdout = None, stdin = sys.stdin.fileno() if isinstance(stdout, str): - stdout = os.open(stdout, os.O_RDWR) + stdout = os.open(stdout, os.O_RDWR|os.O_CREAT) elif isinstance(stdout, int): pass elif stdout is None or not isinstance(stdout, file): stdout = sys.stdout.fileno() if isinstance(stderr, str): - stderr = os.open(stderr, os.O_RDWR) + stderr = os.open(stderr, os.O_RDWR|os.O_CREAT) elif isinstance(stderr, int): pass elif stderr is None or not isinstance(stderr, file): -- cgit From dbb7d2578b694deea96b7ecf0e8c7d46fe8ce762 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 9 Mar 2009 15:10:51 -0400 Subject: Move the mdRaidBootArches logic into the platform module. --- platform.py | 8 ++++++++ storage/__init__.py | 4 +--- storage/devicelibs/mdraid.py | 2 -- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/platform.py b/platform.py index 6db026e3e..2a765cd3f 100644 --- a/platform.py +++ b/platform.py @@ -40,6 +40,7 @@ class Platform(object): _bootloaderPackage = None _diskType = parted.diskType["msdos"] _minimumSector = 0 + _supportsMdRaidBoot = False def __init__(self, anaconda): """Creates a new Platform object. This is basically an abstract class. @@ -121,6 +122,11 @@ class Platform(object): """Return the default platform-specific partitioning information.""" return [("/boot", self.bootFSType, 200, None, 0, 0)] + @property + def supportsMdRaidBoot(self): + """Does the platform support /boot on MD RAID?""" + return self._supportsMdRaidBoot + class EFI(Platform): _diskType = parted.diskType["gpt"] @@ -214,6 +220,7 @@ class IA64(EFI): class PPC(Platform): _bootloaderPackage = "yaboot" _ppcMachine = iutil.getPPCMachine() + _supportsMdRaidBoot = True @property def ppcMachine(self): @@ -319,6 +326,7 @@ class Sparc(Platform): class X86(EFI): _bootloaderPackage = "grub" _isEfi = iutil.isEfi() + _supportsMdRaidBoot = True def __init__(self, anaconda): EFI.__init__(self, anaconda) diff --git a/storage/__init__.py b/storage/__init__.py index 8dda2a421..51a892d8b 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -42,7 +42,6 @@ from formats import getFormat from formats import get_device_format_class from formats import get_default_filesystem_type from devicelibs.lvm import safeLvmName -from devicelibs.mdraid import mdRaidBootArches from udev import udev_trigger import iscsi import zfcp @@ -746,8 +745,7 @@ class Storage(object): "logical volume.")) # most arches can't have boot on RAID - if (boot and boot.type == "mdarray" and - iutil.getArch() not in mdRaidBootArches): + if boot and boot.type == "mdarray" and not self.anaconda.platform.supportsMdRaidBoot: errors.append(_("Bootable partitions cannot be on a RAID " "device.")) diff --git a/storage/devicelibs/mdraid.py b/storage/devicelibs/mdraid.py index 12f429f93..a0d2f6ae3 100644 --- a/storage/devicelibs/mdraid.py +++ b/storage/devicelibs/mdraid.py @@ -28,8 +28,6 @@ from ..errors import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) -mdRaidBootArches = ("i386", "x86_64", "ppc") - def getRaidLevels(): avail = [] try: -- cgit From d741bef735561d637b181f04ffa7da68fbad8e24 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 9 Mar 2009 15:26:38 -0400 Subject: Recognize PS3 as a valid machine type (#489263). --- platform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform.py b/platform.py index 2a765cd3f..a32909400 100644 --- a/platform.py +++ b/platform.py @@ -382,7 +382,7 @@ def getPlatform(anaconda): elif iutil.isPPC(): ppcMachine = iutil.getPPCMachine() - if ppcMachine == "PMac" and iutil.getPPCMacGen() == "NewWorld": + if (ppcMachine == "PMac" and iutil.getPPCMacGen() == "NewWorld") or ppcMachine == "PS3": return NewWorldPPC(anaconda) elif ppcMachine in ["iSeries", "pSeries"]: return IPSeriesPPC(anaconda) -- cgit From 69a1b24cfba7cdab00e0c3bb41c9405117e001d1 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 6 Mar 2009 21:36:24 -0600 Subject: Return early if doAutoPart is False, but clearpart first if kickstart. --- storage/partitioning.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index 78929cf46..5be349554 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -52,8 +52,12 @@ def doAutoPartition(anaconda): anaconda.id.storage.reset() return - # do clearpart - clearPartitions(anaconda.id.storage) + if anaconda.id.storage.doAutoPart or anaconda.isKickstart: + # kickstart uses clearPartitions even without autopart + clearPartitions(anaconda.id.storage) + + if not anaconda.id.storage.doAutoPart: + return # get a list of disks that have at least one free space region of at # least 100MB -- cgit From bf5bdca345f8fe12b3fd63d3bd0da2d3649a58db Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 6 Mar 2009 21:42:02 -0600 Subject: Fix bug keeping track of best free region/type/disk info. --- storage/partitioning.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index 5be349554..7cad2f3fc 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -616,9 +616,6 @@ def allocatePartitions(disks, partitions): # can't allocate any more partitions on this disk log.debug("no free partition slots on %s" % _disk.name) continue - else: - part_type = new_part_type - use_disk = _disk if _part.req_primary and part_type != parted.PARTITION_NORMAL: # we need a primary slot and none are free on this disk @@ -642,7 +639,18 @@ def allocatePartitions(disks, partitions): _part.req_size, best_free=free, boot=_part.req_bootable) - else: + elif best: + if free != best: + # now we know we are choosing a new free space, + # so update the disk and part type + log.debug("updating use_disk to %s (%s), type: %s" + % (_disk, _disk.name, new_part_type)) + part_type = new_part_type + use_disk = _disk + log.debug("new free: %s (%d-%d / %dMB)" % (best, + best.start, + best.end, + best.getSize())) free = best if free and _part.req_bootable: -- cgit From 5359ff7b19e396832bf674e23a73bf21a9f3ad93 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 9 Mar 2009 15:57:18 -0500 Subject: Do a separate disk.commit for each partition add/remove. This means actions will have their advertised granularity, finally. DiskDevice.addPartition and removePartition now operate on a parted.Disk instance that reflects current reality with expectation that the caller will perform a commit on the disk afterwards. --- storage/devices.py | 95 ++++++++++++++++++++++----------------------------- storage/devicetree.py | 2 +- 2 files changed, 42 insertions(+), 55 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 8dc20c132..b69822f62 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -682,6 +682,12 @@ class DiskDevice(StorageDevice): else: raise DeviceUserDeniedFormatError("User prefered to not format.") + # We save the actual state of the disk here. Before the first + # modification (addPartition or removePartition) to the partition + # table we reset self.partedPartition to this state so we can + # perform the modifications one at a time. + self._origPartedDisk = self.partedDisk.duplicate() + self.probe() @property @@ -691,15 +697,42 @@ class DiskDevice(StorageDevice): self._size = self.partedDisk.device.getSize() return self._size - def setPartedDisk(self, disk): - log_method_call(self, self.name, disk_path=disk.device.path) - self.partedDisk = disk + def resetPartedDisk(self): + """ Reset parted.Disk to reflect the actual layout of the disk. """ + log_method_call(self, self.name) + self.partedDisk = self._origPartedDisk def removePartition(self, device): + log_method_call(self, self.name, part=device.name) + if self.partedDisk != self._origPartedDisk: + log.debug("going to reset self.partedDisk to actual state") + self.resetPartedDisk() + partition = self.partedDisk.getPartitionByPath(device.path) if partition: self.partedDisk.removePartition(partition) + def addPartition(self, device): + log_method_call(self, self.name, part=device.name) + if self.partedDisk != self._origPartedDisk: + log.debug("going to reset self.partedDisk to actual state") + self.resetPartedDisk() + + for part in self.partedDisk.partitions: + log.debug("disk %s: partition %s has geom %s" % (self.name, + part.getDeviceNodeName(), + part.geometry)) + + geometry = device.partedPartition.geometry + # XXX at some point we need to improve precision of non-grow sizes + #constraint = parted.Constraint(exactGeom=geometry, + # device=self.partedDisk.device) + partition = parted.Partition(disk=self.partedDisk, + type=device.partedPartition.type, + geometry=geometry) + self.partedDisk.addPartition(partition, + constraint=self.partedDisk.device.getConstraint()) + def probe(self): """ Probe for any missing information about this device. @@ -759,26 +792,6 @@ class PartitionDevice(StorageDevice): only type we are concerned with is primary/logical/extended. Usage specification is accomplished through the use of flags, which we will set according to the partition's format. - - - On usage of parted... - - We are doing this in a slightly lame way right now. We do all - partitioning on the disks' partedDisk instances and update the - partitions' partedPartition instances accordingly. What this means - is that when the time comes to commit changes, all a - PartitionDevice must do to make the changes take effect is make - sure self.disk.partedDisk.commit() is called. Probably better - would be to work with copies of the parted.Disks during - partitioning, save the modified parted.Partitions in the - PartitionDevice instances, and call - self.disk.partedDisk.deletePartition in destroy and - self.disk.partedDisk.addPartition, followed by - self.disk.partedDisk.commit() in create. - - OTOH, the upside to the current approach is that both the disks' - and the partitions' parted instances are in agreement in - reflecting the future layout. """ _type = "partition" _resizable = True @@ -903,16 +916,11 @@ class PartitionDevice(StorageDevice): return self.partType & parted.PARTITION_PROTECTED def _getPartedPartition(self): - ppart = None - if self._partedPartition and self.disk and \ - self.disk.partedDisk == self._partedPartition.disk: - ppart = self._partedPartition - elif self.disk: + if self.disk and not self._partedPartition: pdisk = self.disk.partedDisk - ppart = pdisk.getPartitionByPath(self.path) - self._partedPartition = ppart + self._partedPartition = pdisk.getPartitionByPath(self.path) - return ppart + return self._partedPartition def _setPartedPartition(self, partition): """ Set this PartitionDevice's parted Partition instance. """ @@ -950,10 +958,6 @@ class PartitionDevice(StorageDevice): log_method_call(self, self.name) StorageDevice._setFormat(self, format) - # now, set the flags on our parted.Partition - #if format: - # self.partedPartitions - def setBootable(self, bootable): """ Set the bootable flag for this partition. """ if self.partedPartition: @@ -1013,19 +1017,6 @@ class PartitionDevice(StorageDevice): if not self.exists: return - # build a dict of this partition's parted flags - """ - XXX removed temporarily to make things faster - for flag in parted.partitionFlag.keys(): - if self.partedPartition.isFlagAvailable(flag): - self.partedFlags[flag] = self.partedPartition.getFlag(flag) - - if self.partedFlags[parted.PARTITION_BOOT]: - self.bootable = True - else: - self.bootable = False - """ - # this is in MB self._size = self.partedPartition.getSize() self.targetSize = self._size @@ -1055,6 +1046,7 @@ class PartitionDevice(StorageDevice): if self.bootable: self.setFlag(parted.PARTITION_BOOT) + self.disk.addPartition(self) self.disk.commit() self.exists = True self.setup() @@ -1075,12 +1067,7 @@ class PartitionDevice(StorageDevice): if self.status: self.format.destroy() - # We have already removed the partition from the in-memory - # parted.Disk, so there is nothing to do here except make sure - # self.disk.partedDisk.commit() gets called. - # - # We must have faith that there are no erroneous outstanding - # changes also queued for the disk. + self.disk.removePartition(self) self.disk.commit() self.exists = False diff --git a/storage/devicetree.py b/storage/devicetree.py index 2a9d62ea2..84fcc8a6f 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -477,7 +477,7 @@ class DeviceTree(object): # if this is a partition we need to remove it from the parted.Disk if isinstance(dev, PartitionDevice): - dev.disk.removePartition(dev) + dev.disk.partedDisk.removePartition(dev.partedPartition) self._devices.remove(dev) log.debug("removed %s (%s) from device tree" % (dev.name, -- cgit From af62f32dff290807137000a5a68483245e73e901 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 9 Mar 2009 16:01:46 -0500 Subject: Rewrite action sort so it works correctly. Action sort previously had several holes in it. Instead of trying to fix it, I rewrote it. It works at least as well as the old version in my testing. --- storage/devicetree.py | 231 +++++++++++++++++++++++++++++--------------------- 1 file changed, 133 insertions(+), 98 deletions(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index 84fcc8a6f..b0694e50d 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -330,114 +330,149 @@ class DeviceTree(object): def processActions(self, dryRun=None): """ Execute all registered actions. """ - def cmpActions(x, y): - """ - < 1 => x < y - 0 => x == y - > 1 => x > y - - FIXME: this is unmanageable. - """ - #log.debug("%s | %s" % (x, y)) - - # destroy actions come first - if x.isDestroy() and not y.isDestroy(): - #log.debug("x is destroy -- x first") - return -1 - elif y.isDestroy() and not x.isDestroy(): - #log.debug("y is destroy -- y first") - return 1 - elif x.isDestroy() and y.isDestroy(): - # outermost/youngest first - if x.device.dependsOn(y.device): - #log.debug("x depends on y -- x first") - return -1 - elif y.device.dependsOn(x.device): - #log.debug("y depends on x -- y first") - return 1 - # filesystem destruction must precede device destruction - elif x.isFormat() and not y.isFormat(): - #log.debug("x is format -- x first") - return -1 - elif y.isFormat() and not x.isFormat(): - #log.debug("y is format -- y first") - return 1 - - # resize actions come next - if x.isResize() and not y.isResize(): - #log.debug("x is resize -- x first") - return -1 - elif y.isResize() and not x.isResize(): - #log.debug("y is resize -- y first") - return 1 - elif x.isResize() and y.isResize(): - if x.isGrow() and y.isGrow(): - # for grow, devices come first, root down - if x.isDevice() and not y.isDevice(): - #log.debug("x is device -- x first") - return -1 - elif y.isDevice() and not x.isDevice(): - #log.debug("y is device -- y first") - return 1 + # in most cases the actions will already be sorted because of the + # rules for registration, but let's not rely on that + def cmpActions(a1, a2): + ret = 0 + if a1.isDestroy() and a2.isDestroy(): + if a1.device.path == a2.device.path: + # if it's the same device, destroy the format first + if a1.isFormat() and a2.isFormat(): + ret = 0 + elif a1.isFormat() and not a2.isFormat(): + ret = -1 + elif not a1.isFormat() and a2.isFormat(): + ret = 1 + elif a1.device.dependsOn(a2.device): + ret = -1 + elif a2.device.dependsOn(a1.device): + ret = 1 + # generally destroy partitions after lvs, vgs, &c + elif isinstance(a1.device, PartitionDevice) and \ + isinstance(a2.device, PartitionDevice): + ret = cmp(a2.device.name, a1.device.name) + elif isinstance(a1.device, PartitionDevice) and \ + not isinstance(a2.device, DiskDevice): + ret = 1 + elif isinstance(a2.device, PartitionDevice) and \ + not isinstance(a1.device, DiskDevice): + ret = -1 + else: + ret = cmp(a2.device.name, a1.device.name) + elif a1.isDestroy(): + ret = -1 + elif a2.isDestroy(): + ret = 1 + elif a1.isResize() and a2.isResize(): + if a1.device.path == a2.device.path: + if a1.obj and a2.obj: + ret = 0 + elif a1.isFormat() and not a2.isFormat(): + # same path, one device, one format + if a1.isGrow(): + ret = 1 + else: + ret = -1 + elif not a1.isFormat() and a2.isFormat(): + # same path, one device, one format + if a1.isGrow(): + ret = -1 + else: + ret = 1 else: - # innermost/oldest first - if x.device.dependsOn(y.device): - #log.debug("x depends on y -- y first") - return 1 - elif y.device.dependsOn(x.device): - #log.debug("y depends on x -- x first") - return -1 - elif x.isShrink() and y.isShrink(): - # for shrink, filesystems come first, leaves up - if x.isFormat() and not y.isFormat(): - #log.debug("x is format -- x first") - return -1 - elif y.isFormat() and not x.isFormat(): - #log.debug("y is format -- y first") - return 1 + ret = cmp(a1.device.name, a2.device.name) + elif a1.device.dependsOn(a2.device): + if a1.isGrow(): + ret = 1 + else: + ret = -1 + elif a2.device.dependsOn(a1.device): + if a1.isGrow(): + ret = -1 + else: + ret = 1 + elif isinstance(a1.device, PartitionDevice) and \ + isinstance(a2.device, PartitionDevice): + ret = cmp(a1.device.name, a2.device.name) + elif isinstance(a1.device, PartitionDevice) and \ + not isinstance(a2.device, DiskDevice): + if a1.isGrow(): + ret = -1 + else: + ret = 1 + elif isinstance(a2.device, PartitionDevice) and \ + not isinstance(a1.device, DiskDevice): + if a2.isGrow(): + ret = 1 + else: + ret = -1 + else: + ret = cmp(a1.device.name, a2.device.name) + elif a1.isResize(): + ret = -1 + elif a2.isResize(): + ret = 1 + elif a1.isCreate() and a2.isCreate(): + if a1.device.path == a2.device.path: + if a1.obj == a2.obj: + ret = 0 + if a1.isFormat(): + ret = 1 + elif a2.isFormat(): + ret = -1 else: - # outermost/youngest first - if x.device.dependsOn(y.device): - #log.debug("x depends on y -- x first") - return -1 - elif y.device.dependsOn(x.device): - #log.debug("y depends on x -- y first") - return 1 + ret = cmp(a1.device.name, a2.device.name) + elif a1.device.dependsOn(a2.device): + ret = 1 + elif a2.device.dependsOn(a1.device): + ret = -1 + # generally create partitions before other device types + elif isinstance(a1.device, PartitionDevice) and \ + isinstance(a2.device, PartitionDevice): + ret = cmp(a1.device.name, a2.device.name) + elif isinstance(a1.device, PartitionDevice) and \ + not isinstance(a2.device, DiskDevice): + ret = -1 + elif isinstance(a2.device, PartitionDevice) and \ + not isinstance(a1.device, DiskDevice): + ret = 1 else: - # we don't care about grow action -v- shrink action - # since they should be unrelated - #log.debug("we don't care") - return 0 - - # create actions come last - if x.isCreate() and y.isCreate(): - # innermost/oldest first - if x.device.dependsOn(y.device): - #log.debug("x depends on y") - return 1 - elif y.device.dependsOn(x.device): - #log.debug("y depends on x") - return -1 - # devices first, root down - elif x.isDevice() and not y.isDevice(): - #log.debug("x is a device") - return -1 - elif y.isDevice() and not x.isDevice(): - #log.debug("y is a device") - return 1 - - #log.debug("no decision") - return 0 + ret = cmp(a1.device.name, a2.device.name) + elif a1.isCreate(): + ret = -1 + elif a2.isCreate(): + ret = 1 + elif a1.isMigrate() and a2.isMigrate(): + if a1.device.path == a2.device.path: + ret = 0 + elif a1.device.dependsOn(a2.device): + ret = 1 + elif a2.device.dependsOn(a1.device): + ret = -1 + elif isinstance(a1.device, PartitionDevice) and \ + isinstance(a2.device, PartitionDevice): + ret = cmp(a1.device.name, a2.device.name) + else: + ret = cmp(a1.device.name, a2.device.name) + else: + ret = cmp(a1.device.name, a2.device.name) + + log.debug("cmp: %d -- %s | %s" % (ret, a1, a2)) + return ret - # in most cases the actions will already be sorted because of the - # rules for registration, but let's not rely on that for action in self._actions: log.debug("action: %s" % action) + log.debug("pruning action queue...") self.pruneActions() for action in self._actions: log.debug("action: %s" % action) - #self._actions.sort(cmpActions) + + log.debug("sorting actions...") + self._actions.sort(cmp=cmpActions) + for action in self._actions: + log.debug("action: %s" % action) + self.teardownAll() for action in self._actions: log.info("executing action: %s" % action) -- cgit From 81e06ba2c61c9974167f1b394bf9c43904d0cfd1 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 9 Mar 2009 16:23:00 -0500 Subject: Prune actions by device based on path, not object-id. Since we instantiate a new Device for most of the "create device" dialogs, we cannot expect to be able to do simple comparison of the instances. --- storage/devicetree.py | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index b0694e50d..1692b0e83 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -183,7 +183,7 @@ class DeviceTree(object): continue # XXX this may finally necessitate object ids - loops = self.findActions(device=a.device, + loops = self.findActions(path=a.device.path, type="destroy", object="device") @@ -192,7 +192,7 @@ class DeviceTree(object): # remove all actions on this device from after the first # destroy up through the last destroy - dev_actions = self.findActions(device=a.device) + dev_actions = self.findActions(path=a.device.path) for rem in dev_actions: start = self._actions.index(a) end = self._actions.index(loops[-1]) @@ -207,7 +207,7 @@ class DeviceTree(object): # iteration of this loop continue - loops = self.findActions(device=a.device, + loops = self.findActions(path=a.device.path, type="create", object="device") @@ -215,7 +215,7 @@ class DeviceTree(object): continue # remove all all actions on this device up to the last create - dev_actions = self.findActions(device=a.device) + dev_actions = self.findActions(path=a.device.path) for rem in dev_actions: end = self._actions.index(loops[-1]) if self._actions.index(rem) < end: @@ -229,7 +229,7 @@ class DeviceTree(object): # iteration of this loop continue - loops = self.findActions(device=a.device, + loops = self.findActions(path=a.device.path, type="resize", object="device") @@ -249,7 +249,7 @@ class DeviceTree(object): # iteration of this loop continue - loops = self.findActions(device=a.device, + loops = self.findActions(path=a.device.path, type="destroy", object="format") @@ -258,7 +258,8 @@ class DeviceTree(object): # remove all actions on this device's format from after the # first destroy up through the last destroy - dev_actions = self.findActions(device=a.device, object="format") + dev_actions = self.findActions(path=a.device.path, + object="format") for rem in dev_actions: start = self._actions.index(a) end = self._actions.index(loops[-1]) @@ -283,7 +284,8 @@ class DeviceTree(object): # remove all all actions on this device's format up to the last # create - dev_actions = self.findActions(device=a.device, object="format") + dev_actions = self.findActions(path=a.device.path, + object="format") for rem in dev_actions: end = self._actions.index(loops[-1]) if self._actions.index(rem) < end: @@ -297,7 +299,7 @@ class DeviceTree(object): # iteration of this loop continue - loops = self.findActions(device=a.device, + loops = self.findActions(path=a.device.path, type="resize", object="format") @@ -317,7 +319,7 @@ class DeviceTree(object): # iteration of this loop continue - loops = self.findActions(device=a.device, + loops = self.findActions(path=a.device.path, type="migrate", object="format") @@ -569,7 +571,7 @@ class DeviceTree(object): # add the device back into the tree self._addDevice(action.device) - def findActions(self, device=None, type=None, object=None): + def findActions(self, device=None, type=None, object=None, path=None): """ Find all actions that match all specified parameters. Keyword arguments: @@ -577,6 +579,7 @@ class DeviceTree(object): device -- device to match (Device, or None to match any) type -- action type to match (string, or None to match any) object -- operand type to match (string, or None to match any) + path -- device path to match (string, or None to match any) """ if device is None and type is None and object is None: @@ -596,6 +599,9 @@ class DeviceTree(object): if _object is not None and action.obj != _object: continue + + if path is not None and action.device.path != path: + continue actions.append(action) -- cgit From 299e8228d6b2ed9b3ee47e0ec28d98fae42c1aec Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 9 Mar 2009 16:31:44 -0500 Subject: Only populate the device tree on demand. We don't really want the tree populated when we instantiate it, so don't populate it until someone calls its populate method. --- storage/__init__.py | 2 ++ storage/devicetree.py | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 51a892d8b..4d8c13bcf 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -167,6 +167,7 @@ class Storage(object): self.defaultFSType = get_default_filesystem_type() self.defaultBootFSType = get_default_filesystem_type(boot=True) + # these will both be empty until our reset method gets called self.devicetree = DeviceTree(intf=self.anaconda.intf, ignored=self.ignoredDisks, exclusive=self.exclusiveDisks, @@ -216,6 +217,7 @@ class Storage(object): zeroMbr=self.zeroMbr, passphrase=self.encryptionPassphrase, luksDict=self.__luksDevs) + self.devicetree.populate() self.fsset = FSSet(self.devicetree) w.pop() diff --git a/storage/devicetree.py b/storage/devicetree.py index 1692b0e83..7bd9ecc1d 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -170,8 +170,6 @@ class DeviceTree(object): if luksDict and isinstance(luksDict, dict): self.__luksDevs = luksDict - self._populate() - def pruneActions(self): """ Prune loops and redundant actions from the queue. """ # handle device destroy actions @@ -1101,7 +1099,7 @@ class DeviceTree(object): log.info("setup of %s failed: %s" % (lv_device.name, e)) - def _populate(self): + def populate(self): """ Locate all storage devices. """ # each iteration scans any devices that have appeared since the # previous iteration -- cgit From 41285f06d0ab846a695ffbe0b5306b1682c8859f Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 9 Mar 2009 19:43:44 -0500 Subject: Fix several minor bugs preventing upgrade/rescue mount. (#488946) --- iw/upgrade_migratefs_gui.py | 2 +- storage/__init__.py | 4 +++- upgrade.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/iw/upgrade_migratefs_gui.py b/iw/upgrade_migratefs_gui.py index d71e3d8bc..42397cfaf 100644 --- a/iw/upgrade_migratefs_gui.py +++ b/iw/upgrade_migratefs_gui.py @@ -57,7 +57,7 @@ class UpgradeMigrateFSWindow (InstallWindow): def getScreen (self, anaconda): self.devicetree = anaconda.id.storage.devicetree self.fsset = anaconda.id.storage.fsset - self.migent = self.fsset.getMigratableEntries() + self.migent = self.fsset.migratableDevices box = gtk.VBox (False, 5) box.set_border_width (5) diff --git a/storage/__init__.py b/storage/__init__.py index 4d8c13bcf..a28e921a5 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -32,6 +32,7 @@ import isys import iutil from constants import * from pykickstart.constants import * +from flags import flags import storage_log from errors import * @@ -864,10 +865,11 @@ def findExistingRootDevices(anaconda, upgradeany=False): return rootDevs -def mountExistingSystem(anaconda, rootDevice, +def mountExistingSystem(anaconda, rootEnt, allowDirty=None, warnDirty=None, readOnly=None): """ Mount filesystems specified in rootDevice's /etc/fstab file. """ + rootDevice = rootEnt[0] rootPath = anaconda.rootPath fsset = anaconda.id.storage.fsset if readOnly: diff --git a/upgrade.py b/upgrade.py index fc025a5d6..7cb2f5dab 100644 --- a/upgrade.py +++ b/upgrade.py @@ -163,7 +163,7 @@ def bindMountDevDirectory(instPath): # returns None if no filesystem exist to migrate def upgradeMigrateFind(anaconda): - migents = anaconda.id.storage.fsset.getMigratableEntries() + migents = anaconda.id.storage.fsset.migratableDevices if not migents or len(migents) < 1: anaconda.dispatch.skipStep("upgrademigratefs") else: -- cgit From 7db25498c16f455316d6065212542e820c39f972 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 9 Mar 2009 19:45:07 -0500 Subject: Clean up handling of /proc, /sys, /dev/pts, /dev/shm entries. Just drop them when parsing /etc/fstab -- that way we can create them before mounting the system the same way for upgrade, rescue, and install. --- storage/__init__.py | 9 ++++----- storage/formats/fs.py | 1 + 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index a28e921a5..63fa622d9 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1080,14 +1080,13 @@ class FSSet(object): self.cryptTab = None self.blkidTab = None self.active = False - self._tmpfs = None + self._devpts = None self._sysfs = None self._proc = None self._devshm = None @property def sysfs(self): - self._sysfs = self.mountpoints.get("/sys") if not self._sysfs: self._sysfs = NoDevice(format=getFormat("sysfs", device="sys", @@ -1096,7 +1095,6 @@ class FSSet(object): @property def devpts(self): - self._devpts = self.mountpoints.get("/dev/pts") if not self._devpts: self._devpts = NoDevice(format=getFormat("devpts", device="devpts", @@ -1105,7 +1103,6 @@ class FSSet(object): @property def proc(self): - self._proc = self.mountpoints.get("/proc") if not self._proc: self._proc = NoDevice(format=getFormat("proc", device="proc", @@ -1114,7 +1111,6 @@ class FSSet(object): @property def devshm(self): - self._devshm = self.mountpoints.get("/dev/shm") if not self._devshm: self._devshm = NoDevice(format=getFormat("tmpfs", device="tmpfs", @@ -1233,6 +1229,9 @@ class FSSet(object): device.format = getFormat("bind", device=device.path, exists=True) + elif mountpoint in ("/proc", "/sys", "/dev/shm", "/dev/pts"): + # drop these now -- we'll recreate later + continue else: # nodev filesystem -- preserve or drop completely? format = getFormat(fstype) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 2cc35bb67..65641bea9 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -981,6 +981,7 @@ class NoDevFS(FS): def __init__(self, *args, **kwargs): FS.__init__(self, *args, **kwargs) self.exists = True + self.device = self.type def _setDevice(self, devspec): self._device = devspec -- cgit From 65542dd664c9a2d7d2c6af66dc6ecda2ea1544b3 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 9 Mar 2009 19:47:34 -0500 Subject: Move the recursive teardown of all devices out of processActions. For upgrades we won't want to tear down everything, so do it in turnOnFilesystems instead, since we're already doing several things in there based on upgrade -v- install. --- packages.py | 1 + storage/devicetree.py | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/packages.py b/packages.py index 2dd562d6a..26eb7dfa0 100644 --- a/packages.py +++ b/packages.py @@ -103,6 +103,7 @@ def turnOnFilesystems(anaconda): iutil.execWithRedirect("swapoff", ["-a"], stdout = "/dev/tty5", stderr="/dev/tty5", searchPath = 1) + anaconda.id.storage.devicetree.teardownAll() upgrade_migrate = False if anaconda.id.upgrade: diff --git a/storage/devicetree.py b/storage/devicetree.py index 7bd9ecc1d..4729810d0 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -473,7 +473,6 @@ class DeviceTree(object): for action in self._actions: log.debug("action: %s" % action) - self.teardownAll() for action in self._actions: log.info("executing action: %s" % action) if not dryRun: -- cgit From 22c8b4a9144dc157b503eb26e3f542383c883546 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Mon, 9 Mar 2009 18:39:00 -1000 Subject: New version. --- anaconda.spec | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index c6ac69596..d2c07b563 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.25 +Version: 11.5.0.26 Release: 1 License: GPLv2+ Group: Applications/System @@ -209,6 +209,37 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Mon Mar 09 2009 David Cantrell - 11.5.0.26-1 +- Move the recursive teardown of all devices out of processActions. (dlehman) +- Clean up handling of /proc, /sys, /dev/pts, /dev/shm entries. (dlehman) +- Fix several minor bugs preventing upgrade/rescue mount. (#488946) (dlehman) +- Only populate the device tree on demand. (dlehman) +- Prune actions by device based on path, not object-id. (dlehman) +- Rewrite action sort so it works correctly. (dlehman) +- Do a separate disk.commit for each partition add/remove. (dlehman) +- Fix bug keeping track of best free region/type/disk info. (dlehman) +- Return early if doAutoPart is False, but clearpart first if kickstart. + (dlehman) +- Recognize PS3 as a valid machine type (#489263). (clumens) +- Move the mdRaidBootArches logic into the platform module. (clumens) +- stdout and stderr may also need to be created. (clumens) +- Fix booty for dmraid (hdegoede) +- It's self.origrequest, not self.origreqest (#489036). (clumens) +- Added crypto.py unittest; Updated devicelibs tests baseclass.py and lvm.py + (mgracik) +- Start storage before parsing the kickstart file. (clumens) +- Make sure autopart without any clearpart command will fail. (clumens) +- Update storage flag on ks autopart (rvykydal) +- Use correct storage attribute for ks clearpart (rvykydal) +- Catch the new _ped.DiskLabelException for unrecognized disklabels. + (dlehman) +- Catch all failures from making parted objects in exceptionDisks. (dlehman) +- various dmraid fixes. (jgranado) +- Implement the format disk question as a callback. (jgranado) +- Add dmraid functionality to new storage code. (jgranado) +- Do not pass None values into nonmandatory arguments, you are screwing the + default values.. (msivak) + * Thu Mar 05 2009 David Cantrell - 11.5.0.25-1 - Schedule device destroy actions for partitions last. (dlehman) - Pass storage.disks, not storage, to createAllowed.... (#488860) (dlehman) -- cgit From 02821c4cb57396c2eb4e74e7b463f5830878ee8c Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Tue, 10 Mar 2009 13:59:50 +0100 Subject: We are searching a list, not a dict now Related commit: 4503335c6f5d33bcb236da1757123dde73a842bc. --- kickstart.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kickstart.py b/kickstart.py index 9e95fdbde..9c106a32d 100644 --- a/kickstart.py +++ b/kickstart.py @@ -602,9 +602,9 @@ class Partition(commands.partition.F9_Partition): if pd.start != 0 and pd.disk == "": raise KickstartValueError, formatErrorMsg(self.lineno, msg="Partition command with start cylinder requires a drive specification") hds = map(lambda x: x.name, filter(lambda x: isys.mediaPresent(x.name), self.handler.id.storage.disks)) - if not hds.has_key(pd.disk) and hds.has_key('mapper/'+pd.disk): + if pd.disk not in hds and pd.disk in ('mapper/'+hd for hd in hds): pd.disk = 'mapper/' + pd.disk - if pd.disk != "" and pd.disk not in hds.keys(): + if pd.disk not in hds: raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in partition command" % pd.disk) request = partRequests.PartitionSpec(filesystem, -- cgit From ad45eb81594164d85f22b0143e4514ccbbeef940 Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Mon, 9 Mar 2009 15:12:22 +0100 Subject: Use the pyblock functions when possible. * storage/devicelibs/dm.py : For each function that is used in the storage code, we try to use pyblock first. We fail to calling dmsetup. * storage/devices.py : Erased the commented line that had the pyblock call. We are doing all the pyblock calls from storage/devicelibs/dm.py. * storage/devicetree.py : likewise. * storage/formats/dmraid.py : likewise. --- storage/devicelibs/dm.py | 9 +++++++++ storage/devices.py | 2 -- storage/devicetree.py | 2 -- storage/formats/dmraid.py | 1 - 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/storage/devicelibs/dm.py b/storage/devicelibs/dm.py index 74d8228de..29df1266e 100644 --- a/storage/devicelibs/dm.py +++ b/storage/devicelibs/dm.py @@ -22,6 +22,7 @@ import os +import block import iutil from ..errors import * @@ -32,6 +33,10 @@ import logging log = logging.getLogger("storage") def name_from_dm_node(dm_node): + name = block.getNameFromDmNode(dm_node) + if name is not None: + return name + st = os.stat("/dev/%s" % dm_node) major = os.major(st.st_rdev) minor = os.minor(st.st_rdev) @@ -44,6 +49,10 @@ def name_from_dm_node(dm_node): return name.strip() def dm_node_from_name(map_name): + dm_node = block.getDmNodeFromName(map_name) + if dm_node is not None: + return dm_node + devnum = iutil.execWithCapture("dmsetup", ["info", "--columns", "--noheadings", diff --git a/storage/devices.py b/storage/devices.py index b69822f62..678ed62f9 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -98,7 +98,6 @@ import math # device backend modules from devicelibs import mdraid from devicelibs import lvm -#import block from devicelibs import dm import parted import _ped @@ -1217,7 +1216,6 @@ class DMDevice(StorageDevice): raise DeviceError("device has not been created") return dm.dm_node_from_name(self.name) - #return block.getDmNodeFromName(self.name) def _setName(self, name): """ Set the device's map name. """ diff --git a/storage/devicetree.py b/storage/devicetree.py index 4729810d0..ccc925a83 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -712,7 +712,6 @@ class DeviceTree(object): for slave_name in slave_names: # if it's a dm-X name, resolve it to a map name first if slave_name.startswith("dm-"): - #slave_name = block.getNameFromDmNode(slave_name) slave_name = dm.name_from_dm_node(slave_name) slave_dev = self.getDeviceByName(slave_name) if slave_dev: @@ -766,7 +765,6 @@ class DeviceTree(object): for slave_name in slave_names: # if it's a dm-X name, resolve it to a map name if slave_name.startswith("dm-"): - #slave_name = block.getNameFromDmNode(slave_name) slave_name = dm.name_from_dm_node(slave_name) slave_dev = self.getDeviceByName(slave_name) if slave_dev: diff --git a/storage/formats/dmraid.py b/storage/formats/dmraid.py index ef80902e2..f5f28084a 100644 --- a/storage/formats/dmraid.py +++ b/storage/formats/dmraid.py @@ -23,7 +23,6 @@ import block from iutil import log_method_call -#from dm import dm_node_from_name from ..errors import * from . import DeviceFormat, register_device_format -- cgit From 5201e7be923c2010a01d65c395fccf5d28d92fef Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Tue, 10 Mar 2009 21:22:57 +0100 Subject: Fix pruning between two destroy actions on the same device --- storage/devicetree.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index ccc925a83..2821e428a 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -196,6 +196,9 @@ class DeviceTree(object): end = self._actions.index(loops[-1]) if start < self._actions.index(rem) <= end: self._actions.remove(rem) + # last destroy action removed + if rem == loops[-1]: + break # device create actions actions = self.findActions(type="create", object="device") @@ -579,7 +582,7 @@ class DeviceTree(object): path -- device path to match (string, or None to match any) """ - if device is None and type is None and object is None: + if device is None and type is None and object is None and path is None: return self._actions[:] # convert the string arguments to the types used in actions -- cgit From 8a3ac49804621c72b3e884678387acecc0bb42f6 Mon Sep 17 00:00:00 2001 From: Jesse Keating Date: Tue, 10 Mar 2009 13:43:51 -0700 Subject: Fix a typo --- booty/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/booty/__init__.py b/booty/__init__.py index 328128fdf..fc34a56e3 100644 --- a/booty/__init__.py +++ b/booty/__init__.py @@ -46,7 +46,7 @@ def getBootloader(storage): import x86 return x86.x86BootloaderInfo(storage) elif rhpl.getArch() == "ppc": - import pcc + import ppc return ppc.ppcBootloaderInfo(storage) elif rhpl.getArch() == "sparc": import sparc -- cgit From 01408c6a8b2d2672d22cae26ebd1b593263d8dcb Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 10 Mar 2009 18:38:33 -0500 Subject: Destruction of the member device formatting will be handled elsewhere. Even though destruction of the array is dependent upon the destruction of the member devices' metadata, we screw everything up if we try to reach down and do from here. If actions are scheduled correctly we will not have any problems. --- storage/devices.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 678ed62f9..be604f56c 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -2134,10 +2134,9 @@ class MDRaidArrayDevice(StorageDevice): self.format.destroy() self.teardown() - # destruction of the formats on members will destroy the array - for disk in self.devices: - disk.format.destroy() + # The destruction of the formatting on the member devices does the + # real work, but it isn't our place to do it from here. self.exists = False -- cgit From f37a69c451f7ce1f4ddb8d0a3b5e0077a8b3efc7 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 10 Mar 2009 18:41:01 -0500 Subject: Fix name collision between formats.mdraid and devicelibs.mdraid. --- iw/raid_dialog_gui.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index efca453a7..d45b582ed 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -28,7 +28,7 @@ import gtk import datacombo import gui -import storage.devicelibs.mdraid +import storage.devicelibs.mdraid as mdraidlib from storage.devices import * from storage.deviceaction import * from partition_ui_helpers_gui import * @@ -113,7 +113,7 @@ class RaidEditor: def raidlevelchangeCB(self, widget, sparesb): raidlevel = widget.get_model()[widget.get_active()][0] numparts = sparesb.get_data("numparts") - maxspares = raid.get_raid_max_spares(raidlevel, numparts) + maxspares = mdraidlib.get_raid_max_spares(raidlevel, numparts) if maxspares > 0 and raidlevel != "raid0": adj = sparesb.get_adjustment() @@ -398,8 +398,8 @@ class RaidEditor: nspares = 0 if origrequest.level: - maxspares = raid.get_raid_max_spares(origrequest.level, - numparts) + maxspares = mdraidlib.get_raid_max_spares(origrequest.level, + numparts) else: maxspares = 0 @@ -418,7 +418,7 @@ class RaidEditor: if not origrequest.exists: - self.levelcombo = self.createRaidLevelMenu(storage.devicelibs.mdraid.raid_levels, + self.levelcombo = self.createRaidLevelMenu(mdraidlib.raid_levels, origrequest.level) lbl.set_mnemonic_widget(self.levelcombo) else: -- cgit From 762833a97acdd26dd3cdb9ccd341f96dcd5005a6 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 10 Mar 2009 18:42:23 -0500 Subject: Create partitions with exactly the geometry we calculate. If we use the device constraint as before, libparted will align the start and end to a cylinder boundary which, on my disks, means a granularity of 7MB. Not good enough. Plus, for mdraid we need >= 1% variation in size between members of an array. We can get this with cylinder-aligned partitions, but we'll have to do the alignment ourselves and continue to use exact constraints. --- storage/devices.py | 6 ++---- storage/partitioning.py | 11 ++++++++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index be604f56c..713449b7e 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -723,14 +723,12 @@ class DiskDevice(StorageDevice): part.geometry)) geometry = device.partedPartition.geometry - # XXX at some point we need to improve precision of non-grow sizes - #constraint = parted.Constraint(exactGeom=geometry, - # device=self.partedDisk.device) + constraint = parted.Constraint(exactGeom=geometry) partition = parted.Partition(disk=self.partedDisk, type=device.partedPartition.type, geometry=geometry) self.partedDisk.addPartition(partition, - constraint=self.partedDisk.device.getConstraint()) + constraint=constraint) def probe(self): """ Probe for any missing information about this device. diff --git a/storage/partitioning.py b/storage/partitioning.py index 7cad2f3fc..c10440145 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -507,6 +507,10 @@ def doPartitioning(storage, exclusiveDisks=None): disks = [d for d in disks if d.name in exclusiveDisks] partitions = storage.partitions + for part in partitions: + if not part.exists: + # start over with flexible-size requests + part.req_size = part.req_base_size # FIXME: isn't there a better place for this to happen? try: @@ -698,18 +702,19 @@ def allocatePartitions(disks, partitions): # create minimum geometry for this request # req_size is in MB + sectors_per_track = disk.device.biosGeometry[2] length = (_part.req_size * (1024 * 1024)) / sectorSize new_geom = parted.Geometry(device=disk.device, - start=free.start, + start=max(sectors_per_track, free.start), length=length) # create the partition and add it to the disk partition = parted.Partition(disk=disk, type=part_type, geometry=new_geom) + constraint = parted.Constraint(exactGeom=new_geom) disk.addPartition(partition=partition, - constraint=disk.device.getConstraint()) -# constraint=parted.Constraint(device=disk.device)) + constraint=constraint) log.debug("created partition %s of %dMB and added it to %s" % (partition.getDeviceNodeName(), partition.getSize(), disk)) # this one sets the name -- cgit From 3dee346394ca52f2c5bef4fe8ee16b60cd2cfab0 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 10 Mar 2009 18:45:54 -0500 Subject: Speed up partitioning screen redraws by trimming workload where possible. Previously we would call doPartitioning every time we refreshed the main partitioning screen. This makes little sense if the user just finished modifying a vg or md array or only assigned a mountpoint to an existing device. There is still more that we could do, like only calling doPartitioning if one of the generated action involves partition allocation. --- iw/partition_gui.py | 88 +++++++++++++++++++++++++++++------------------------ 1 file changed, 48 insertions(+), 40 deletions(-) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index 9dae43657..d58e13989 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -976,7 +976,13 @@ class PartitionWindow(InstallWindow): elif doDeleteDevice(self.intf, self.storage, device): - self.refresh() + if isinstance(device, storage.devices.DiskDevice) or \ + isinstance(device, storage.devices.PartitionDevice): + justRedraw = False + else: + justRedraw = True + + self.refresh(justRedraw=justRedraw) def resetCB(self, *args): if not confirmResetPartitionState(self.intf): @@ -987,45 +993,49 @@ class PartitionWindow(InstallWindow): self.tree.clear() self.populate() - def refresh(self): + def refresh(self, justRedraw=None): + log.debug("refresh: justRedraw=%s" % justRedraw) self.diskStripeGraph.shutDown() self.tree.clear() - try: - doPartitioning(self.storage) + if justRedraw: rc = 0 - except PartitioningError, msg: - self.intf.messageWindow(_("Error Partitioning"), - _("Could not allocate requested partitions: %s.") % (msg), - custom_icon="error") - rc = -1 - except PartitioningWarning, msg: - # XXX somebody other than me should make this look better - # XXX this doesn't handle the 'delete /boot partition spec' case - # (it says 'add anyway') - dialog = gtk.MessageDialog(self.parent, 0, gtk.MESSAGE_WARNING, - gtk.BUTTONS_NONE, - _("Warning: %s.") % (msg)) - gui.addFrame(dialog) - button = gtk.Button(_("_Modify Partition")) - dialog.add_action_widget(button, 1) - button = gtk.Button(_("_Continue")) - dialog.add_action_widget(button, 2) - dialog.set_position(gtk.WIN_POS_CENTER) - - dialog.show_all() - rc = dialog.run() - dialog.destroy() - - if rc == 1: - rc = -1 - else: + else: + try: + doPartitioning(self.storage) rc = 0 - all_devices = self.storage.devicetree.devices.values() - bootDevs = [d for d in all_devices if d.bootable] - #if reqs: - # for req in reqs: - # req.ignoreBootConstraints = 1 + except PartitioningError, msg: + self.intf.messageWindow(_("Error Partitioning"), + _("Could not allocate requested partitions: %s.") % (msg), + custom_icon="error") + rc = -1 + except PartitioningWarning, msg: + # XXX somebody other than me should make this look better + # XXX this doesn't handle the 'delete /boot partition spec' case + # (it says 'add anyway') + dialog = gtk.MessageDialog(self.parent, 0, gtk.MESSAGE_WARNING, + gtk.BUTTONS_NONE, + _("Warning: %s.") % (msg)) + gui.addFrame(dialog) + button = gtk.Button(_("_Modify Partition")) + dialog.add_action_widget(button, 1) + button = gtk.Button(_("_Continue")) + dialog.add_action_widget(button, 2) + dialog.set_position(gtk.WIN_POS_CENTER) + + dialog.show_all() + rc = dialog.run() + dialog.destroy() + + if rc == 1: + rc = -1 + else: + rc = 0 + all_devices = self.storage.devicetree.devices.values() + bootDevs = [d for d in all_devices if d.bootable] + #if reqs: + # for req in reqs: + # req.ignoreBootConstraints = 1 if not rc == -1: self.populate() @@ -1064,8 +1074,6 @@ class PartitionWindow(InstallWindow): self.parent, raiddev, isNew) - # is a shallow copy enough? - #origpartitions = self.storage.devicetree.copy() while 1: actions = raideditor.run() @@ -1074,7 +1082,7 @@ class PartitionWindow(InstallWindow): # FIXME: this needs to handle exceptions self.storage.devicetree.registerAction(action) - if self.refresh(): + if self.refresh(justRedraw=True): actions.reverse() for action in actions: self.storage.devicetree.cancelAction(action) @@ -1102,7 +1110,7 @@ class PartitionWindow(InstallWindow): # XXX we should handle exceptions here self.anaconda.id.storage.devicetree.registerAction(action) - if self.refresh(): + if self.refresh(justRedraw=not actions): # autopart failed -- cancel the actions and try to get # back to previous state actions.reverse() @@ -1135,7 +1143,7 @@ class PartitionWindow(InstallWindow): # FIXME: handle exceptions self.storage.devicetree.registerAction(action) - if self.refresh(): + if self.refresh(justRedraw=True): actions.reverse() for action in actions: self.storage.devicetree.cancelAction(action) -- cgit From d23288c6ed5082026d1b28347e2d8f26d9dd0e93 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 10 Mar 2009 19:54:27 -0500 Subject: Add a size attribute to mdraid arrays. It doesn't account for metadata size, but that's supposed to be something like 128K, so I'm hoping to get away without it. --- storage/devices.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/storage/devices.py b/storage/devices.py index 713449b7e..2ae643cc6 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -1878,6 +1878,14 @@ class MDRaidArrayDevice(StorageDevice): #self.probe() + @property + def size(self): + size = None + for device in self.devices: + if size is None or device.size < size: + size = device.size + return size + @property def mdadmConfEntry(self): """ This array's mdadm.conf entry. """ -- cgit From e9c1cf1abe72e9874558796461288e68b87b924c Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 10 Mar 2009 19:57:48 -0500 Subject: Schedule destruction of any existing formatting along with the device. Schedule destruction of any existing formats the caller neglected to destroy before calling Storage.destroyDevice to avoid things like mdadm hanging the install trying to ask if we're sure we want to make an array on what looks like an ext3 filesystem. --- storage/__init__.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/storage/__init__.py b/storage/__init__.py index 63fa622d9..1e1aa7fe1 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -568,6 +568,10 @@ class Storage(object): def destroyDevice(self, device): """ Schedule destruction of a device. """ + if device.format.exists: + # schedule destruction of any formatting while we're at it + self.devicetree.registerAction(ActionDestroyFormat(device)) + action = ActionDestroyDevice(device) self.devicetree.registerAction(action) -- cgit From db374beb154743e5c81bd0366906ce183cdee609 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 10 Mar 2009 20:00:25 -0500 Subject: Fix action pruning to handle more complex scenarios. Added logic to handle additional cases like destroy loops for devices the did not exist to begin with. Also, I tested it more thoroughly this time. --- storage/devicetree.py | 213 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 172 insertions(+), 41 deletions(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index 2821e428a..e974717e8 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -180,25 +180,53 @@ class DeviceTree(object): # iteration of this loop continue - # XXX this may finally necessitate object ids - loops = self.findActions(path=a.device.path, - type="destroy", - object="device") - - if len(loops) == 1: + log.debug("action '%s' (%s)" % (a, id(a))) + destroys = self.findActions(path=a.device.path, + type="destroy", + object="device") + + creates = self.findActions(path=a.device.path, + type="create", + object="device") + + # If the device is not preexisting, we remove all actions up + # to and including the last destroy action. + # If the device is preexisting, we remove all actions from + # after the first destroy action up to and including the last + # destroy action. + loops = [] + first_destroy_idx = None + first_create_idx = None + stop_action = None + start = None + if len(destroys) > 1: + # there are multiple destroy actions for this device + loops = destroys + first_destroy_idx = self._actions.index(loops[0]) + start = self._actions.index(a) + 1 + stop_action = destroys[-1] + + if creates: + first_create_idx = self._actions.index(creates[0]) + if not loops or first_destroy_idx > first_create_idx: + # this device is not preexisting + start = first_create_idx + stop_action = destroys[-1] + + if start is None: continue - # remove all actions on this device from after the first - # destroy up through the last destroy + # now we remove all actions on this device between the start + # index (into self._actions) and stop_action. dev_actions = self.findActions(path=a.device.path) for rem in dev_actions: - start = self._actions.index(a) - end = self._actions.index(loops[-1]) - if start < self._actions.index(rem) <= end: + end = self._actions.index(stop_action) + if start <= self._actions.index(rem) <= end: + log.debug(" removing action '%s' (%s)" % (rem, id(rem))) self._actions.remove(rem) - # last destroy action removed - if rem == loops[-1]: - break + + if rem == stop_action: + break # device create actions actions = self.findActions(type="create", object="device") @@ -208,18 +236,51 @@ class DeviceTree(object): # iteration of this loop continue - loops = self.findActions(path=a.device.path, - type="create", - object="device") - - if len(loops) == 1: + log.debug("action '%s' (%s)" % (a, id(a))) + creates = self.findActions(path=a.device.path, + type="create", + object="device") + + destroys = self.findActions(path=a.device.path, + type="destroy", + object="device") + + # If the device is preexisting, we remove everything between + # the first destroy and the last create. + # If the device is not preexisting, we remove everything up to + # the last create. + loops = [] + first_destroy_idx = None + first_create_idx = None + stop_action = None + start = None + if len(creates) > 1: + # there are multiple create actions for this device + loops = creates + first_create_idx = self._actions.index(loops[0]) + start = 0 + stop_action = creates[-1] + + if destroys: + first_destroy_idx = self._actions.index(destroys[0]) + if not loops or first_create_idx > first_destroy_idx: + # this device is preexisting + start = first_destroy_idx + 1 + stop_action = creates[-1] + + if start is None: continue - # remove all all actions on this device up to the last create + # remove all actions on this from after the first destroy up + # to the last create dev_actions = self.findActions(path=a.device.path) for rem in dev_actions: - end = self._actions.index(loops[-1]) - if self._actions.index(rem) < end: + if rem == stop_action: + break + + end = self._actions.index(stop_action) + if start <= self._actions.index(rem) < end: + log.debug(" removing action '%s' (%s)" % (rem, id(rem))) self._actions.remove(rem) # device resize actions @@ -230,6 +291,7 @@ class DeviceTree(object): # iteration of this loop continue + log.debug("action '%s' (%s)" % (a, id(a))) loops = self.findActions(path=a.device.path, type="resize", object="device") @@ -239,6 +301,7 @@ class DeviceTree(object): # remove all but the last resize action on this device for rem in loops[:-1]: + log.debug(" removing action '%s' (%s)" % (rem, id(rem))) self._actions.remove(rem) # format destroy @@ -250,23 +313,55 @@ class DeviceTree(object): # iteration of this loop continue - loops = self.findActions(path=a.device.path, - type="destroy", - object="format") - - if len(loops) == 1: + log.debug("action '%s' (%s)" % (a, id(a))) + destroys = self.findActions(path=a.device.path, + type="destroy", + object="format") + + creates = self.findActions(path=a.device.path, + type="create", + object="format") + + # If the format is not preexisting, we remove all actions up + # to and including the last destroy action. + # If the format is preexisting, we remove all actions from + # after the first destroy action up to and including the last + # destroy action. + loops = [] + first_destroy_idx = None + first_create_idx = None + stop_action = None + start = None + if len(destroys) > 1: + # there are multiple destroy actions for this format + loops = destroys + first_destroy_idx = self._actions.index(loops[0]) + start = self._actions.index(a) + 1 + stop_action = destroys[-1] + + if creates: + first_create_idx = self._actions.index(creates[0]) + if not loops or first_destroy_idx > first_create_idx: + # this format is not preexisting + start = first_create_idx + stop_action = destroys[-1] + + if start is None: continue - # remove all actions on this device's format from after the - # first destroy up through the last destroy + # now we remove all actions on this device's format between + # the start index (into self._actions) and stop_action. dev_actions = self.findActions(path=a.device.path, object="format") for rem in dev_actions: - start = self._actions.index(a) - end = self._actions.index(loops[-1]) - if start < self._actions.index(rem) <= end: + end = self._actions.index(stop_action) + if start <= self._actions.index(rem) <= end: + log.debug(" removing action '%s' (%s)" % (rem, id(rem))) self._actions.remove(rem) + if rem == stop_action: + break + # format create # XXX I don't think there's a way for these loops to happen actions = self.findActions(type="create", object="format") @@ -276,20 +371,52 @@ class DeviceTree(object): # iteration of this loop continue - loops = self.findActions(device=a.device, - type="create", - object="format") - - if len(loops) == 1: + log.debug("action '%s' (%s)" % (a, id(a))) + creates = self.findActions(path=a.device.path, + type="create", + object="format") + + destroys = self.findActions(path=a.device.path, + type="destroy", + object="format") + + # If the format is preexisting, we remove everything between + # the first destroy and the last create. + # If the format is not preexisting, we remove everything up to + # the last create. + loops = [] + first_destroy_idx = None + first_create_idx = None + stop_action = None + start = None + if len(creates) > 1: + # there are multiple create actions for this format + loops = creates + first_create_idx = self._actions.index(loops[0]) + start = 0 + stop_action = creates[-1] + + if destroys: + first_destroy_idx = self._actions.index(destroys[0]) + if not loops or first_create_idx > first_destroy_idx: + # this format is preexisting + start = first_destroy_idx + 1 + stop_action = creates[-1] + + if start is None: continue - # remove all all actions on this device's format up to the last - # create + # remove all actions on this from after the first destroy up + # to the last create dev_actions = self.findActions(path=a.device.path, object="format") for rem in dev_actions: - end = self._actions.index(loops[-1]) - if self._actions.index(rem) < end: + if rem == stop_action: + break + + end = self._actions.index(stop_action) + if start <= self._actions.index(rem) < end: + log.debug(" removing action '%s' (%s)" % (rem, id(rem))) self._actions.remove(rem) # format resize @@ -300,6 +427,7 @@ class DeviceTree(object): # iteration of this loop continue + log.debug("action '%s' (%s)" % (a, id(a))) loops = self.findActions(path=a.device.path, type="resize", object="format") @@ -309,6 +437,7 @@ class DeviceTree(object): # remove all but the last resize action on this format for rem in loops[:-1]: + log.debug(" removing action '%s' (%s)" % (rem, id(rem))) self._actions.remove(rem) # format migrate @@ -320,6 +449,7 @@ class DeviceTree(object): # iteration of this loop continue + log.debug("action '%s' (%s)" % (a, id(a))) loops = self.findActions(path=a.device.path, type="migrate", object="format") @@ -329,6 +459,7 @@ class DeviceTree(object): # remove all but the last migrate action on this format for rem in loops[:-1]: + log.debug(" removing action '%s' (%s)" % (rem, id(rem))) self._actions.remove(rem) def processActions(self, dryRun=None): -- cgit From 286bb4ad445b60405c39cf804f9a1363bc55a152 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 10 Mar 2009 16:05:05 -1000 Subject: New version. --- anaconda.spec | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index d2c07b563..7cee0753a 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.26 +Version: 11.5.0.27 Release: 1 License: GPLv2+ Group: Applications/System @@ -209,6 +209,22 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Tue Mar 10 2009 David Cantrell - 11.5.0.27-1 +- Fix action pruning to handle more complex scenarios. (dlehman) +- Schedule destruction of any existing formatting along with the device. + (dlehman) +- Add a size attribute to mdraid arrays. (dlehman) +- Speed up partitioning screen redraws by trimming workload where possible. + (dlehman) +- Create partitions with exactly the geometry we calculate. (dlehman) +- Fix name collision between formats.mdraid and devicelibs.mdraid. (dlehman) +- Destruction of the member device formatting will be handled elsewhere. + (dlehman) +- Fix a typo (jkeating) +- Fix pruning between two destroy actions on the same device (rvykydal) +- Use the pyblock functions when possible. (jgranado) +- We are searching a list, not a dict now (rvykydal) + * Mon Mar 09 2009 David Cantrell - 11.5.0.26-1 - Move the recursive teardown of all devices out of processActions. (dlehman) - Clean up handling of /proc, /sys, /dev/pts, /dev/shm entries. (dlehman) -- cgit From adf5421a1f5953b1848eb2cc3733a5d1f61ed294 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 10 Mar 2009 09:41:45 -1000 Subject: Fix _getCheckArgs() in class FS. _getCheckArgs() needs to return argv. In doCheck(), replace argv with a call to self._getCheckArgs(). --- storage/formats/fs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 65641bea9..144e8fd43 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -367,6 +367,7 @@ class FS(DeviceFormat): argv = [] argv.extend(self.defaultCheckOptions) argv.append(self.device) + return argv def doCheck(self, intf=None): if not self.exists: @@ -387,7 +388,7 @@ class FS(DeviceFormat): try: rc = iutil.execWithPulseProgress(self.fsckProg, - argv, + self._getCheckArgs(), stdout="/dev/tty5", stderr="/dev/tty5", progress = w) -- cgit From a50bfe30889e1e7d4820bcdb5cb747453d466ca1 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 10 Mar 2009 14:14:44 -1000 Subject: Hook up 'Shrink current system' dialog to new storage code. The whichToResize() window presents the user with a list of resizable filesystems, allows the user to enter a new target size, then creates an ActionResizeFormat object and returns that to the caller. The action is registered in the Storage object. --- iw/autopart_type.py | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/iw/autopart_type.py b/iw/autopart_type.py index 700d7da0e..b634cc964 100644 --- a/iw/autopart_type.py +++ b/iw/autopart_type.py @@ -32,6 +32,7 @@ from iw_gui import * from flags import flags import network from storage import iscsi +from storage.deviceaction import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -74,11 +75,13 @@ def whichToResize(storage, intf): if not part.exists: continue - if part.resizable: + # Resize the following storage types: + # resizable filesystem (e.g., ext3 or ntfs) on resizable partition + if part.resizable and part.format.resizable: i = store.append(None) store[i] = ("%s (%s, %d MB)" %(part.name, part.format.name, - math.floor(part.size)), + math.floor(part.format.currentSize)), part) if part.targetSize is not None: combo.set_active_iter(i) @@ -106,10 +109,11 @@ def whichToResize(storage, intf): dialog.destroy() return rc - req = getActive(combo) - req.targetSize = dxml.get_widget("resizeSB").get_value_as_int() + request = getActive(combo) + newSize = dxml.get_widget("resizeSB").get_value_as_int() + action = ActionResizeFormat(request, newSize) dialog.destroy() - return rc + return (rc, action) class PartitionTypeWindow(InstallWindow): def __init__(self, ics): @@ -130,8 +134,10 @@ class PartitionTypeWindow(InstallWindow): self.dispatch.skipStep("bootloader", skip = 0) else: if val == -2: - rc = whichToResize(self.storage, self.intf) - if rc != gtk.RESPONSE_OK: + (rc, action) = whichToResize(self.storage, self.intf) + if rc == gtk.RESPONSE_OK: + self.storage.devicetree.registerAction(action) + else: raise gui.StayOnScreen # we're not going to delete any partitions in the resize case -- cgit From 3034f3fd3f2459eabe30f3c9fd8187a3fae75807 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 10 Mar 2009 20:20:46 +0100 Subject: Fix anaconda udev rules to not require pre-existing device nodes The way the anaconda udev rules are currently written, they require the device nodes to already be present when they run. This is not always the case (as udev creates the nodes after running the rules). This patch fixes this, which fixes the recognition of lvm VG's and LV's on iscsi disks. --- 70-anaconda.rules | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/70-anaconda.rules b/70-anaconda.rules index 97d0d2381..42ac1d43d 100644 --- a/70-anaconda.rules +++ b/70-anaconda.rules @@ -18,10 +18,10 @@ ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+ LABEL="anaconda_raid_probe" # probe raid metadata of mdraid member devices -ENV{ID_FS_TYPE}=="linux_raid_member", IMPORT{program}="/usr/sbin/mdadm --examine --export $root/%k" +ENV{ID_FS_TYPE}=="linux_raid_member", IMPORT{program}="/usr/sbin/mdadm --examine --export $tempnode" # probe metadata of LVM2 physical volumes -ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/usr/sbin/lvm pvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -opv_name,pv_uuid,pv_size,vg_name,vg_uuid,pv_pe_count,pv_pe_alloc_count,pe_start $root/%k" +ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/usr/sbin/lvm pvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -opv_name,pv_uuid,pv_size,vg_name,vg_uuid,pv_pe_count,pv_pe_alloc_count,pe_start $tempnode" ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/usr/sbin/lvm vgs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -ouuid,size,free,extent_size,extent_count,free_count,pv_count $env{LVM2_VG_NAME}" ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/usr/sbin/lvm lvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -olv_name,lv_uuid,lv_size $env{LVM2_VG_NAME}" -- cgit From 37a23a7c937240437e9db965e688f8bf0b0877be Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 11 Mar 2009 14:44:13 -0400 Subject: Initialize storage in rescue mode so we can find roots (#488984). --- rescue.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/rescue.py b/rescue.py index 3006971a8..ffdd6a822 100644 --- a/rescue.py +++ b/rescue.py @@ -103,7 +103,7 @@ class RescueInterface: # moderately useful. def makeMtab(instPath, fsset): try: - f = open("/etc/mtab", "w+") + f = open(instPath + "/etc/mtab", "w+") except IOError, e: log.info("failed to open /etc/mtab for write: %s" % e) return @@ -255,6 +255,9 @@ def runRescue(anaconda, instClass): else: readOnly = 0 + import storage + storage.storageInitialize(anaconda) + disks = upgrade.findExistingRoots(anaconda, upgradeany = 1) if not disks: -- cgit From 4d33bbb4729e10a19905a63a0dc0df222cf6469c Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 11 Mar 2009 17:06:55 -0500 Subject: Make device teardown methods more resilient. Only perform teardown actions if device status indicates that the device is active. Also, do not throw an exception for non-existent devices if doing a recursive teardown. This gives a chance for lower-level devices to still get deactivated even if the higher-level devices have been replaced. --- storage/devices.py | 57 +++++++++++++++--------------------------------------- 1 file changed, 16 insertions(+), 41 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 2ae643cc6..b80eb7600 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -513,10 +513,11 @@ class StorageDevice(Device): def teardown(self, recursive=None): """ Close, or tear down, a device. """ log_method_call(self, self.name, status=self.status) - if not self.exists: + if not self.exists and not recursive: raise DeviceError("device has not been created") - self.format.teardown() + if self.status and self.format.exists: + self.format.teardown() if recursive: self.teardownParents(recursive=recursive) @@ -773,12 +774,6 @@ class DiskDevice(StorageDevice): if not os.path.exists(self.path): raise DeviceError("device does not exist") - def teardown(self, recursive=False): - """ Close, or tear down, a device. """ - log_method_call(self, self.name, status=self.status) - if not self.exists: - raise DeviceError("device has not been created") - class PartitionDevice(StorageDevice): """ A disk partition. @@ -1227,18 +1222,6 @@ class DMDevice(StorageDevice): name = property(lambda d: d._name, lambda d,n: d._setName(n)) - def teardown(self, recursive=None): - """ Close, or tear down, a device. """ - log_method_call(self, self.name, status=self.status) - if not self.exists: - raise DeviceError("device has not been created") - - if self.status: - self.format.teardown() - - if recursive: - self.teardownParents(recursive=recursive) - class DMCryptDevice(DMDevice): """ A dm-crypt device """ @@ -1321,11 +1304,13 @@ class LUKSDevice(DMCryptDevice): def teardown(self, recursive=False): """ Close, or tear down, a device. """ log_method_call(self, self.name, status=self.status) - if not self.exists: + if not self.exists and not recursive: raise DeviceError("device has not been created") - if self.status: + if self.status and self.format.exists: self.format.teardown() + + if self.slave.format.exists: self.slave.format.teardown() if recursive: @@ -1502,7 +1487,7 @@ class LVMVolumeGroupDevice(DMDevice): def teardown(self, recursive=None): """ Close, or tear down, a device. """ log_method_call(self, self.name, status=self.status) - if not self.exists: + if not self.exists and not recursive: raise DeviceError("device has not been created") if self.status: @@ -1779,11 +1764,13 @@ class LVMLogicalVolumeDevice(DMDevice): def teardown(self, recursive=None): """ Close, or tear down, a device. """ log_method_call(self, self.name, status=self.status) - if not self.exists: + if not self.exists and not recursive: raise DeviceError("device has not been created") - if self.status: + if self.status and self.format.exists: self.format.teardown() + + if self.status: lvm.lvdeactivate(self.vg.name, self._name) if recursive: @@ -2088,11 +2075,13 @@ class MDRaidArrayDevice(StorageDevice): def teardown(self, recursive=None): """ Close, or tear down, a device. """ log_method_call(self, self.name, status=self.status) - if not self.exists: + if not self.exists and not recursive: raise DeviceError("device has not been created") - if self.status: + if self.status and self.format.exists: self.format.teardown() + + if self.status: mdraid.mddeactivate(self.path) if recursive: @@ -2233,10 +2222,6 @@ class DMRaidArrayDevice(DiskDevice): def getDMNode(self): DMDevice.getDMNode(self) - def teardown(self, recursive=None): - # avoid DiskDevice's overriding of teardown() - StorageDevice.teardown(self, recursive) - class DMRaidPartitionDevice(PartitionDevice): """ A disk partition in a dmraid array, identical to a general @@ -2513,16 +2498,6 @@ class OpticalDevice(StorageDevice): major=major, minor=minor, exists=True, parents=parents, sysfsPath=sysfsPath) - def teardown(self, recursive=None): - """ Close, or tear down, a device. """ - log_method_call(self, self.name, status=self.status) - if not self.exists: - raise DeviceError("device has not been created") - - self.format.teardown() - if recursive: - self.teardownParents(recursive=recursive) - def mediaPresent(self): """ Return a boolean indicating whether or not the device contains media. -- cgit From 37669f8a61c57380f13cbe3246c6af6548513d6c Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 11 Mar 2009 17:11:44 -0500 Subject: Make sure we use the newly committed parted.Partition after create. We added created a new parted.Partition to add to the disk, so now our _partedPartition attribute is outdated. Setting the attribute to None forces a lookup on the next access, which will sort us out. --- storage/devices.py | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index b80eb7600..cb0ba38f7 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -1024,22 +1024,12 @@ class PartitionDevice(StorageDevice): self.createParents() self.setupParents() - # If we are bootable or our format has a flag it needs set, do - # that now. - # - # FIXME: this should be moved onto a common codepath so that - # flags get set properly for existing partitions as well. - # - # XXX this may trigger multiple parted.Disk.commit() calls, but - # who cares? - if self.format.partedFlag is not None: - self.setFlag(self.format.partedFlag) - - if self.bootable: - self.setFlag(parted.PARTITION_BOOT) - self.disk.addPartition(self) self.disk.commit() + + # this will force a lookup on next access, which we want + self.partedPartition = None + self.exists = True self.setup() -- cgit From f54d2b88ae7fbfd0d16341b09037da81684abdc3 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 11 Mar 2009 17:14:31 -0500 Subject: Set partition flags in format create/destroy execute methods. This way, we will have correct flags whether we are creating the partitions or just creating/removing the formats. --- storage/deviceaction.py | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/storage/deviceaction.py b/storage/deviceaction.py index a30314955..7d6e8367f 100644 --- a/storage/deviceaction.py +++ b/storage/deviceaction.py @@ -21,6 +21,10 @@ # Red Hat Author(s): Dave Lehman # +from parted import PARTITION_BOOT + +from udev import udev_settle + from devices import StorageDevice, PartitionDevice from formats import getFormat from errors import * @@ -259,8 +263,16 @@ class ActionCreateFormat(DeviceAction): self.origFormat = getFormat(None) def execute(self, intf=None): - # XXX we should set partition type flag as needed - # - or should that be in the CreateDevice action? + if isinstance(self.device, PartitionDevice): + if self.format.partedFlag is not None: + self.device.setFlag(self.format.partedFlag) + self.device.disk.commit() + + if self.device.bootable: + self.device.setFlag(PARTITION_BOOT) + self.device.disk.commit() + + udev_settle() self.device.setup() self.device.format.create(intf=intf, device=self.device.path, @@ -292,8 +304,17 @@ class ActionDestroyFormat(DeviceAction): def execute(self, intf=None): """ wipe the filesystem signature from the device """ if self.origFormat: + if isinstance(self.device, PartitionDevice) and \ + self.origFormat.partedFlag is not None: + # unset partition flags and commit + self.device.unsetFlag(self.origFormat.partedFlag) + self.device.disk.commit() + + udev_settle() + self.device.setup() self.origFormat.destroy() + udev_settle() self.device.teardown() def cancel(self): -- cgit From 2e11711f0e8af8c4b4098d8490751210e64eb6d4 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 11 Mar 2009 17:16:39 -0500 Subject: Try again to set up LVs when we've just added a new PV to the VG. If we do find all of the PVs this will cause the LVs to be activated, which will allow us to probe them. --- storage/devicetree.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/storage/devicetree.py b/storage/devicetree.py index e974717e8..976d5d4e5 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1174,6 +1174,11 @@ class DeviceTree(object): vg_device = self.getDeviceByName(vg_name) if vg_device: vg_device._addDevice(device) + for lv in vg_device.lvs: + try: + lv.setup() + except DeviceError as e: + log.info("setup of %s failed: %s" % (lv.name, e)) else: try: vg_uuid = udev_device_get_vg_uuid(info) -- cgit From 9e9626be46747052f4346989df4cb8e14fe94ccb Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 11 Mar 2009 17:18:27 -0500 Subject: Handle the case of removing an unallocated partition from the tree. This was causing a traceback after clicking "ok" on the dialog stating that partition allocation failed due to insufficient free space, adding insult to injury. --- storage/devicetree.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index 976d5d4e5..7288ad12b 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -644,7 +644,9 @@ class DeviceTree(object): raise ValueError("Cannot remove non-leaf device '%s'" % dev.name) # if this is a partition we need to remove it from the parted.Disk - if isinstance(dev, PartitionDevice): + if isinstance(dev, PartitionDevice) and dev.disk is not None: + # if this partition hasn't been allocated it could not have + # a disk attribute dev.disk.partedDisk.removePartition(dev.partedPartition) self._devices.remove(dev) -- cgit From b7863af1f3f4e927aa96827144c5efe161e71a8b Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 11 Mar 2009 17:21:35 -0500 Subject: Deactivate devices after we've finished scanning them. There's no good reason to leave everything active, and we certainly don't want everything to be active once the partitioning screen comes up. --- storage/devicetree.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/storage/devicetree.py b/storage/devicetree.py index 7288ad12b..d21883d4d 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1266,6 +1266,8 @@ class DeviceTree(object): for dev in devices: self.addUdevDevice(dev) + self.teardownAll() + def teardownAll(self): """ Run teardown methods on all devices. """ for device in self.leaves: -- cgit From 952b189742d09dcdca1c62a97d366b9188b78636 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 11 Mar 2009 17:27:15 -0500 Subject: Be explicit about resetting Disks' partedDisk attribute. (#489678) We hit a case where the projected layout matches exactly the preexisting layout, and so the equality test gives a false positive due to pyparted's sneaky comparison methods. This happens when you do autopart two or more times in a row on a system. --- storage/devices.py | 8 -------- storage/devicetree.py | 5 +++++ 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index cb0ba38f7..ae1ad0918 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -704,20 +704,12 @@ class DiskDevice(StorageDevice): def removePartition(self, device): log_method_call(self, self.name, part=device.name) - if self.partedDisk != self._origPartedDisk: - log.debug("going to reset self.partedDisk to actual state") - self.resetPartedDisk() - partition = self.partedDisk.getPartitionByPath(device.path) if partition: self.partedDisk.removePartition(partition) def addPartition(self, device): log_method_call(self, self.name, part=device.name) - if self.partedDisk != self._origPartedDisk: - log.debug("going to reset self.partedDisk to actual state") - self.resetPartedDisk() - for part in self.partedDisk.partitions: log.debug("disk %s: partition %s has geom %s" % (self.name, part.getDeviceNodeName(), diff --git a/storage/devicetree.py b/storage/devicetree.py index d21883d4d..2a40fc423 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -607,6 +607,11 @@ class DeviceTree(object): for action in self._actions: log.debug("action: %s" % action) + log.debug("resetting parted disks...") + for device in self.devices.itervalues(): + if isinstance(device, DiskDevice): + device.resetPartedDisk() + for action in self._actions: log.info("executing action: %s" % action) if not dryRun: -- cgit From ff0c74b2e993277804b4f0e0f517211bca573820 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 11 Mar 2009 18:47:45 -0500 Subject: Modify livecd.py to work with new storage backend. --- livecd.py | 41 +++++++++++++++++++---------------------- storage/__init__.py | 5 ++++- 2 files changed, 23 insertions(+), 23 deletions(-) diff --git a/livecd.py b/livecd.py index 695f241d6..216b77eae 100644 --- a/livecd.py +++ b/livecd.py @@ -147,8 +147,8 @@ class LiveCDCopyBackend(backend.AnacondaBackend): def postAction(self, anaconda): self._unmountNonFstabDirs(anaconda) try: - anaconda.id.fsset.umountFilesystems(anaconda.rootPath, - swapoff = False) + anaconda.id.storage.fsset.umountFilesystems(anaconda.rootPath, + swapoff = False) os.rmdir(anaconda.rootPath) except Exception, e: log.error("Unable to unmount filesystems.") @@ -158,7 +158,8 @@ class LiveCDCopyBackend(backend.AnacondaBackend): self._unmountNonFstabDirs(anaconda) return - anaconda.id.fsset.umountFilesystems(anaconda.rootPath, swapoff = False) + anaconda.id.storage.fsset.umountFilesystems(anaconda.rootPath, + swapoff = False) def doInstall(self, anaconda): log.info("Preparing to install packages") @@ -173,7 +174,7 @@ class LiveCDCopyBackend(backend.AnacondaBackend): osimg = self._getLiveBlockDevice() # the real image osfd = os.open(osimg, os.O_RDONLY) - rootDevice = anaconda.id.fsset.rootDevice + rootDevice = anaconda.id.storage.fsset.rootDevice rootDevice.setup() rootfd = os.open(rootDevice.path, os.O_WRONLY) @@ -223,10 +224,10 @@ class LiveCDCopyBackend(backend.AnacondaBackend): self._resizeRootfs(anaconda, wait) # remount filesystems - anaconda.id.fsset.mountFilesystems(anaconda) + anaconda.id.storage.fsset.mountFilesystems(anaconda) # restore the label of / to what we think it is - rootDevice = anaconda.id.fsset.rootDevice + rootDevice = anaconda.id.storage.fsset.rootDevice rootDevice.setup() # ensure we have a random UUID on the rootfs # FIXME: this should be abstracted per filesystem type @@ -243,14 +244,10 @@ class LiveCDCopyBackend(backend.AnacondaBackend): # this is pretty distasteful, but should work with things like # having a separate /usr/local - # XXX wow, what in the hell is going on here? - # get a list of fsset entries that are relevant - entries = sorted(filter(lambda e: not e.fsystem.isKernelFS() and \ - e.getMountPoint(), anaconda.id.fsset.entries)) # now create a tree so that we know what's mounted under where fsdict = {"/": []} - for entry in entries: - tocopy = entry.getMountPoint() + for entry in anaconda.id.storage.fsset.mountpoints.itervalues(): + tocopy = entry.format.mountpoint if tocopy.startswith("/mnt") or tocopy == "swap": continue keys = sorted(fsdict.keys(), reverse = True) @@ -266,8 +263,8 @@ class LiveCDCopyBackend(backend.AnacondaBackend): if tocopy in copied: continue copied.append(tocopy) - copied.extend(map(lambda x: x.getMountPoint(), fsdict[tocopy])) - entry = anaconda.id.fsset.getEntryByMountPoint(tocopy) + copied.extend(map(lambda x: x.format.mountpoint, fsdict[tocopy])) + entry = anaconda.id.storage.fsset.mountpoints[tocopy] # FIXME: all calls to wait.refresh() are kind of a hack... we # should do better about not doing blocking things in the @@ -277,9 +274,9 @@ class LiveCDCopyBackend(backend.AnacondaBackend): # unmount subdirs + this one and then remount under /mnt for e in fsdict[tocopy] + [entry]: - e.umount(anaconda.rootPath) + e.format.teardown() for e in [entry] + fsdict[tocopy]: - e.mount(anaconda.rootPath + "/mnt") + e.format.setup(chroot=anaconda.rootPath + "/mnt") copytree("%s/%s" %(anaconda.rootPath, tocopy), "%s/mnt/%s" %(anaconda.rootPath, tocopy), True, True, @@ -289,14 +286,14 @@ class LiveCDCopyBackend(backend.AnacondaBackend): # mount it back in the correct place for e in fsdict[tocopy] + [entry]: - e.umount(anaconda.rootPath + "/mnt") + e.format.teardown() try: os.rmdir("%s/mnt/%s" %(anaconda.rootPath, - e.getMountPoint())) + e.format.mountpoint)) except OSError, e: log.debug("error removing %s" %(tocopy,)) for e in [entry] + fsdict[tocopy]: - e.mount(anaconda.rootPath) + e.format.setup(chroot=anaconda.rootPath) wait.refresh() @@ -312,7 +309,7 @@ class LiveCDCopyBackend(backend.AnacondaBackend): def _resizeRootfs(self, anaconda, win = None): log.info("going to do resize") - rootDevice = anaconda.id.fsset.rootDevice + rootDevice = anaconda.id.storage.fsset.rootDevice # FIXME: we'd like to have progress here to give an idea of # how long it will take. or at least, to give an indefinite @@ -342,7 +339,7 @@ class LiveCDCopyBackend(backend.AnacondaBackend): anaconda.id.desktop.setDefaultRunLevel(5) # now write out the "real" fstab and mtab - anaconda.id.fsset.write(anaconda.rootPath) + anaconda.id.storage.write(anaconda.rootPath) f = open(anaconda.rootPath + "/etc/mtab", "w+") f.write(anaconda.id.fsset.mtab()) f.close() @@ -373,7 +370,7 @@ class LiveCDCopyBackend(backend.AnacondaBackend): # FIXME: really, this should be in the general sanity checking, but # trying to weave that in is a little tricky at present. ossize = self._getLiveSizeMB() - slash = anaconda.id.fsset.rootDevice + slash = anaconda.id.storage.fsset.rootDevice if slash.size < ossize: rc = anaconda.intf.messageWindow(_("Error"), _("The root filesystem you created is " diff --git a/storage/__init__.py b/storage/__init__.py index 1e1aa7fe1..ac1ff4ec9 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1289,7 +1289,10 @@ class FSSet(object): def mtab(self): format = "%s %s %s %s 0 0\n" mtab = "" - for device in self.devices: + devices = self.mountpoints.values() + self.swapDevices + devices.extend([self.devshm, self.devpts, self.sysfs, self.proc]) + devices.sort(key=lambda d: getattr(d.format, "mountpoint", None)) + for device in devices: if not device.format.status: continue if not device.format.mountable: -- cgit From 78a6d26d2cc8b152efb9030faf2baf6089fb3ac0 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 11 Mar 2009 21:30:26 -0500 Subject: Fix a few bugs in the lvm dialog. (#489022) This time I left out the check prior to setting the vg name as it should be unnecessary. --- iw/lvm_dialog_gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index b3e210101..ad081b204 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -339,7 +339,7 @@ class VolumeGroupEditor: if include: partlist.append_row((device.name, size_string), selected) - if selected: + if selected and device not in self.vg.pvs: self.vg._addPV(device) return (partlist, sw) @@ -548,7 +548,7 @@ class VolumeGroupEditor: # yet if we have not hit 'OK' for the volume group creation if fmt_class().mountable and mountpoint: used = 0 - curmntpt = format.mountpoint + curmntpt = getattr(format, "mountpoint", None) for _lv in self.vg.lvs: if _lv.format.type == "luks": -- cgit From 287102f78b94038595bb0fb79b5de0ee5d7ca8b6 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Wed, 11 Mar 2009 16:37:12 -1000 Subject: New version. --- anaconda.spec | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index 7cee0753a..dccc87822 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.27 +Version: 11.5.0.28 Release: 1 License: GPLv2+ Group: Applications/System @@ -209,6 +209,27 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Wed Mar 11 2009 David Cantrell - 11.5.0.28-1 +- Fix a few bugs in the lvm dialog. (#489022) (dlehman) +- Modify livecd.py to work with new storage backend. (dlehman) +- Be explicit about resetting Disks' partedDisk attribute. (#489678) + (dlehman) +- Deactivate devices after we've finished scanning them. (dlehman) +- Handle the case of removing an unallocated partition from the tree. + (dlehman) +- Try again to set up LVs when we've just added a new PV to the VG. (dlehman) +- Set partition flags in format create/destroy execute methods. (dlehman) +- Make sure we use the newly committed parted.Partition after create. + (dlehman) +- Make device teardown methods more resilient. (dlehman) +- Initialize storage in rescue mode so we can find roots (#488984). (clumens) +- We also need to pack up the extra args tuple, too. (clumens) +- doLoggingSetup keeps growing new arguments, so put them into a dict + (#489709). (clumens) +- Fix anaconda udev rules to not require pre-existing device nodes (hdegoede) +- Hook up 'Shrink current system' dialog to new storage code. (dcantrell) +- Fix _getCheckArgs() in class FS. (dcantrell) + * Tue Mar 10 2009 David Cantrell - 11.5.0.27-1 - Fix action pruning to handle more complex scenarios. (dlehman) - Schedule destruction of any existing formatting along with the device. -- cgit From 2f15ec4b089d4cbcaf9f780fb448312c019d6d72 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 11 Mar 2009 09:34:09 +0100 Subject: If a pv somehow does not contain a vg_name, do not try to get other vg info In the anacond udev rules do not execute the 2 import commands which depend up on the vg_name if we don't have a vg_name. --- 70-anaconda.rules | 1 + 1 file changed, 1 insertion(+) diff --git a/70-anaconda.rules b/70-anaconda.rules index 42ac1d43d..e379a22bd 100644 --- a/70-anaconda.rules +++ b/70-anaconda.rules @@ -22,6 +22,7 @@ ENV{ID_FS_TYPE}=="linux_raid_member", IMPORT{program}="/usr/sbin/mdadm --examine # probe metadata of LVM2 physical volumes ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/usr/sbin/lvm pvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -opv_name,pv_uuid,pv_size,vg_name,vg_uuid,pv_pe_count,pv_pe_alloc_count,pe_start $tempnode" +ENV{LVM2_VG_NAME}!="?*", GOTO="anaconda_end" ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/usr/sbin/lvm vgs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -ouuid,size,free,extent_size,extent_count,free_count,pv_count $env{LVM2_VG_NAME}" ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/usr/sbin/lvm lvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -olv_name,lv_uuid,lv_size $env{LVM2_VG_NAME}" -- cgit From 32c07ed5a3a4f63cfb8fdaba20aff3ac3319f063 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 11 Mar 2009 09:38:15 +0100 Subject: Allow overriding the anaconda udev rules from an updates.img --- anaconda | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/anaconda b/anaconda index fc32dfb65..dc50999f6 100755 --- a/anaconda +++ b/anaconda @@ -170,6 +170,11 @@ def setupPythonUpdates(): f), "/tmp/updates/%s/%s" %(pypkg, f)) + if os.access("/tmp/updates/70-anaconda.rules", os.R_OK): + import shutil + shutil.copyfile("/tmp/updates/70-anaconda.rules", + "/etc/udev/rules.d/70-anaconda.rules") + def parseOptions(): def resolution_cb (option, opt_str, value, parser): parser.values.runres = value -- cgit From a725f97ec45827dac56e922a9f473291fd83dfec Mon Sep 17 00:00:00 2001 From: Martin Gracik Date: Wed, 11 Mar 2009 12:53:53 +0100 Subject: Fixed the format modules import --- storage/formats/__init__.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py index 2b6acae00..3cdc9081d 100644 --- a/storage/formats/__init__.py +++ b/storage/formats/__init__.py @@ -102,11 +102,12 @@ def collect_device_format_classes(): """ dir = os.path.dirname(__file__) for module_file in os.listdir(dir): - if module_file.endswith(".py"): + # make sure we're not importing this module + if module_file.endswith(".py") and module_file != __file__: mod_name = module_file[:-3] - # FIXME: use imputils here instead of exec + # imputil is deprecated in python 2.6 try: - exec("import %s" % mod_name) + globals()[mod_name] = __import__(mod_name) except ImportError, e: log.debug("import of device format module '%s' failed" % mod_name) -- cgit From b40033c68fb1644fe7743338462edf41e5c5b3f8 Mon Sep 17 00:00:00 2001 From: Martin Gracik Date: Thu, 12 Mar 2009 12:46:24 +0100 Subject: Format modules import fix The last patch was importing from a wrong directory --- storage/formats/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py index 3cdc9081d..66e34fb31 100644 --- a/storage/formats/__init__.py +++ b/storage/formats/__init__.py @@ -107,7 +107,7 @@ def collect_device_format_classes(): mod_name = module_file[:-3] # imputil is deprecated in python 2.6 try: - globals()[mod_name] = __import__(mod_name) + globals()[mod_name] = __import__(mod_name, globals(), locals(), [], -1) except ImportError, e: log.debug("import of device format module '%s' failed" % mod_name) -- cgit From 1c479aff318c1a9249135a21095170f7d5d61aa4 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 10 Mar 2009 14:01:50 -0400 Subject: Always go through doAutoPart. We need to do this in order to make sure doPartitioning gets called, which does most of the magic of performing partitioning. We want to break out the autopart stuff into separate functions and only call it in the autopart case. --- storage/partitioning.py | 113 +++++++++++++++++++++++++++--------------------- 1 file changed, 63 insertions(+), 50 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index c10440145..66c0c29b3 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -40,25 +40,7 @@ _ = lambda x: gettext.ldgettext("anaconda", x) import logging log = logging.getLogger("storage") -def doAutoPartition(anaconda): - log.debug("doAutoPartition(%s)" % anaconda) - log.debug("doAutoPart: %s" % anaconda.id.storage.doAutoPart) - log.debug("clearPartType: %s" % anaconda.id.storage.clearPartType) - log.debug("clearPartDisks: %s" % anaconda.id.storage.clearPartDisks) - log.debug("autoPartitionRequests: %s" % anaconda.id.storage.autoPartitionRequests) - log.debug("storage.disks: %s" % anaconda.id.storage.disks) - log.debug("all names: %s" % [d.name for d in anaconda.id.storage.devicetree.devices.values()]) - if anaconda.dir == DISPATCH_BACK: - anaconda.id.storage.reset() - return - - if anaconda.id.storage.doAutoPart or anaconda.isKickstart: - # kickstart uses clearPartitions even without autopart - clearPartitions(anaconda.id.storage) - - if not anaconda.id.storage.doAutoPart: - return - +def _createFreeSpacePartitions(anaconda): # get a list of disks that have at least one free space region of at # least 100MB disks = [] @@ -90,6 +72,9 @@ def doAutoPartition(anaconda): anaconda.id.storage.createDevice(part) devs.append(part) + return (disks, devs) + +def _schedulePartitions(anaconda, disks): # # Convert storage.autoPartitionRequests into Device instances and # schedule them for creation @@ -115,38 +100,9 @@ def doAutoPartition(anaconda): anaconda.id.storage.createDevice(dev) # make sure preexisting broken lvm/raid configs get out of the way + return - # sanity check the individual devices - log.warning("not sanity checking devices because I don't know how yet") - - # run the autopart function to allocate and grow partitions - try: - doPartitioning(anaconda.id.storage, - exclusiveDisks=anaconda.id.storage.clearPartDisks) - except PartitioningWarning as msg: - if not anaconda.isKickstart: - anaconda.intf.messageWindow(_("Warnings During Automatic " - "Partitioning"), - _("Following warnings occurred during automatic " - "partitioning:\n\n%s") % (msg,), - custom_icon='warning') - else: - log.warning(msg) - except PartitioningError as msg: - # restore drives to original state - anaconda.id.storage.reset() - if not anaconda.isKickstart: - extra = "" - anaconda.dispatch.skipStep("partition", skip = 0) - else: - extra = _("\n\nPress 'OK' to exit the installer.") - anaconda.intf.messageWindow(_("Error Partitioning"), - _("Could not allocate requested partitions: \n\n" - "%s.%s") % (msg, extra), custom_icon='error') - - if anaconda.isKickstart: - sys.exit(0) - +def _scheduleLVs(anaconda, devs): if anaconda.id.storage.encryptedAutoPart: pvs = [] for dev in devs: @@ -190,6 +146,63 @@ def doAutoPartition(anaconda): # grow the new VG and its LVs growLVM(anaconda.id.storage) +def doAutoPartition(anaconda): + log.debug("doAutoPartition(%s)" % anaconda) + log.debug("doAutoPart: %s" % anaconda.id.storage.doAutoPart) + log.debug("clearPartType: %s" % anaconda.id.storage.clearPartType) + log.debug("clearPartDisks: %s" % anaconda.id.storage.clearPartDisks) + log.debug("autoPartitionRequests: %s" % anaconda.id.storage.autoPartitionRequests) + log.debug("storage.disks: %s" % anaconda.id.storage.disks) + log.debug("all names: %s" % [d.name for d in anaconda.id.storage.devicetree.devices.values()]) + if anaconda.dir == DISPATCH_BACK: + anaconda.id.storage.reset() + return + + disks = [] + devs = [] + + if anaconda.id.storage.doAutoPart or anaconda.isKickstart: + # kickstart uses clearPartitions even without autopart + clearPartitions(anaconda.id.storage) + + if anaconda.id.storage.doAutoPart: + (disks, devs) = _createFreeSpacePartitions(anaconda) + _schedulePartitions(anaconda, disks) + + # sanity check the individual devices + log.warning("not sanity checking devices because I don't know how yet") + + # run the autopart function to allocate and grow partitions + try: + doPartitioning(anaconda.id.storage, + exclusiveDisks=anaconda.id.storage.clearPartDisks) + except PartitioningWarning as msg: + if not anaconda.isKickstart: + anaconda.intf.messageWindow(_("Warnings During Automatic " + "Partitioning"), + _("Following warnings occurred during automatic " + "partitioning:\n\n%s") % (msg,), + custom_icon='warning') + else: + log.warning(msg) + except PartitioningError as msg: + # restore drives to original state + anaconda.id.storage.reset() + if not anaconda.isKickstart: + extra = "" + anaconda.dispatch.skipStep("partition", skip = 0) + else: + extra = _("\n\nPress 'OK' to exit the installer.") + anaconda.intf.messageWindow(_("Error Partitioning"), + _("Could not allocate requested partitions: \n\n" + "%s.%s") % (msg, extra), custom_icon='error') + + if anaconda.isKickstart: + sys.exit(0) + + if anaconda.id.storage.doAutoPart: + _scheduleLVs(anaconda, dev) + # sanity check the collection of devices log.warning("not sanity checking storage config because I don't know how yet") # now do a full check of the requests -- cgit From d933e3929cf26a9db3317008d100fca2bc245c22 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 11 Mar 2009 11:18:32 -0400 Subject: Clear partitions before scheduling requests. Otherwise, our scheduling requests will get purged by the action loop detector. --- kickstart.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kickstart.py b/kickstart.py index 9c106a32d..b22c5ef57 100644 --- a/kickstart.py +++ b/kickstart.py @@ -18,6 +18,8 @@ # along with this program. If not, see . # +from storage.partitioning import clearPartitions + from errors import * import iutil import isys @@ -243,6 +245,8 @@ class ClearPart(commands.clearpart.FC3_ClearPart): if self.initAll: self.handler.id.storage.reinitializeDisks = self.initAll + clearPartitions(self.handler.id.storage) + return retval class Firewall(commands.firewall.F10_Firewall): -- cgit From 551c11375d045616789f4817cdea681fa155f228 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 11 Mar 2009 16:04:55 -0400 Subject: Don't set default partitioning in every kickstart case. Turns out we don't want to do this because it'll result in the default autopart scheme getting used regardless of what other partitioning commands are specified. --- installclasses/fedora.py | 9 +++++---- installclasses/rhel.py | 10 ++++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/installclasses/fedora.py b/installclasses/fedora.py index 087e2d377..d2ddafa1f 100644 --- a/installclasses/fedora.py +++ b/installclasses/fedora.py @@ -62,10 +62,11 @@ class InstallClass(BaseInstallClass): def setInstallData(self, anaconda): BaseInstallClass.setInstallData(self, anaconda) - BaseInstallClass.setDefaultPartitioning(self, - anaconda.id.storage, - anaconda.platform, - CLEARPART_TYPE_LINUX) + if not anaconda.isKickstart: + BaseInstallClass.setDefaultPartitioning(self, + anaconda.id.storage, + anaconda.platform, + CLEARPART_TYPE_LINUX) def setSteps(self, anaconda): BaseInstallClass.setSteps(self, anaconda); diff --git a/installclasses/rhel.py b/installclasses/rhel.py index 521792862..da25946f3 100644 --- a/installclasses/rhel.py +++ b/installclasses/rhel.py @@ -87,10 +87,12 @@ class InstallClass(BaseInstallClass): def setInstallData(self, anaconda): BaseInstallClass.setInstallData(self, anaconda) - BaseInstallClass.setDefaultPartitioning(self, - anaconda.id.storage, - anaconda.platform, - CLEARPART_TYPE_LINUX) + + if not anaconda.isKickstart: + BaseInstallClass.setDefaultPartitioning(self, + anaconda.id.storage, + anaconda.platform, + CLEARPART_TYPE_LINUX) def setSteps(self, anaconda): dispatch = anaconda.dispatch -- cgit From beea6fd23c92a866e4cfcfd4d9525453bbc75f46 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 11 Mar 2009 16:07:17 -0400 Subject: addPartRequest is no longer needed. The new storage code schedules actions, so we don't need to batch all the requests up to be added later. --- kickstart.py | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/kickstart.py b/kickstart.py index b22c5ef57..fe0353243 100644 --- a/kickstart.py +++ b/kickstart.py @@ -998,24 +998,10 @@ class AnacondaKSParser(KickstartParser): KickstartParser.handleCommand(self, lineno, args) -# this adds a partition to the autopartition list replacing anything -# else with this mountpoint so that you can use autopart and override / -def addPartRequest(anaconda, request): - if not request.mountpoint: - anaconda.id.storage.autoPartitionRequests.append(request) - return - - for req in anaconda.id.storage.autoPartitionRequests: - if req.mountpoint and req.mountpoint == request.mountpoint: - anaconda.id.storage.autoPartitionRequests.remove(req) - break - anaconda.id.storage.autoPartitionRequests.append(request) - def processKickstartFile(anaconda, file): # We need to make sure storage is active before the kickstart file is read. import storage storage.storageInitialize(anaconda) - anaconda.dispatch.skipStep("storageinit") # parse the %pre ksparser = KickstartPreParser(AnacondaKSHandler(anaconda)) @@ -1217,6 +1203,9 @@ def setSteps(anaconda): dispatch.skipStep("installtype") dispatch.skipStep("network") + # Storage is initialized for us right when kickstart processing starts. + dispatch.skipStep("storageinit") + # Don't show confirmation screens on non-interactive installs. if not interactive: dispatch.skipStep("confirminstall") -- cgit From 9c924c843125b19d90a9a3f317eb8d707c1fa108 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 11 Mar 2009 16:09:08 -0400 Subject: Update the logvol command to work with the new storage code. --- kickstart.py | 137 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 85 insertions(+), 52 deletions(-) diff --git a/kickstart.py b/kickstart.py index fe0353243..c798a1d2c 100644 --- a/kickstart.py +++ b/kickstart.py @@ -18,6 +18,9 @@ # along with this program. If not, see . # +from storage.devices import LUKSDevice +from storage.devicelibs.lvm import getPossiblePhysicalExtents +from storage.formats import getFormat from storage.partitioning import clearPartitions from errors import * @@ -334,74 +337,104 @@ class LogVol(commands.logvol.F9_LogVol): def parse(self, args): lvd = commands.logvol.F9_LogVol.parse(self, args) + storage = self.handler.id.storage + devicetree = storage.devicetree + if lvd.mountpoint == "swap": - filesystem = fileSystemTypeGet("swap") + type = "swap" lvd.mountpoint = "" - if lvd.recommended: (lvd.size, lvd.maxSizeMB) = iutil.swapSuggestion() lvd.grow = True else: if lvd.fstype != "": - try: - filesystem = fileSystemTypeGet(lvd.fstype) - except KeyError: - raise KickstartValueError, formatErrorMsg(self.lineno, msg="The \"%s\" filesystem type is not supported." % lvd.fstype) + type = lvd.fstype else: - filesystem = fileSystemTypeGetDefault() + type = storage.defaultFSType - # sanity check mountpoint + # Sanity check mountpoint if lvd.mountpoint != "" and lvd.mountpoint[0] != '/': raise KickstartValueError, formatErrorMsg(self.lineno, msg="The mount point \"%s\" is not valid." % (lvd.mountpoint,)) - try: - vgid = self.handler.ksVGMapping[lvd.vgname] - except KeyError: - raise KickstartValueError, formatErrorMsg(self.lineno, msg="No volume group exists with the name '%s'. Specify volume groups before logical volumes." % lvd.vgname) - - for areq in self.handler.id.storage.autoPartitionRequests: - if areq.type == REQUEST_LV: - if areq.volumeGroup == vgid and areq.logicalVolumeName == lvd.name: - raise KickstartValueError, formatErrorMsg(self.lineno, msg="Logical volume name already used in volume group %s" % lvd.vgname) - elif areq.type == REQUEST_VG and areq.uniqueID == vgid: - # Store a reference to the VG so we can do the PE size check. - vg = areq - - if not self.handler.ksVGMapping.has_key(lvd.vgname): - raise KickstartValueError, formatErrorMsg(self.lineno, msg="Logical volume specifies a non-existent volume group" % lvd.name) - - if lvd.percent == 0 and not lvd.preexist: - if lvd.size == 0: - raise KickstartValueError, formatErrorMsg(self.lineno, msg="Size required") - elif not lvd.grow and lvd.size*1024 < vg.pesize: - raise KickstartValueError, formatErrorMsg(self.lineno, msg="Logical volume size must be larger than the volume group physical extent size.") - elif (lvd.percent <= 0 or lvd.percent > 100) and not lvd.preexist: - raise KickstartValueError, formatErrorMsg(self.lineno, msg="Percentage must be between 0 and 100") - - request = partRequests.LogicalVolumeRequestSpec(filesystem, - format = lvd.format, - mountpoint = lvd.mountpoint, - size = lvd.size, - percent = lvd.percent, - volgroup = vgid, - lvname = lvd.name, - grow = lvd.grow, - maxSizeMB = lvd.maxSizeMB, - preexist = lvd.preexist, - fsprofile = lvd.fsprofile) - - if lvd.fsopts != "": - request.fsopts = lvd.fsopts + # Check that the VG this LV is a member of has already been specified. + vg = devicetree.getDeviceByName(lvd.vgname) + if not vg: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="No volume group exists with the name \"%s\". Specify volume groups before logical volumes." % lvd.vgname) + + # If this specifies an existing request that we should not format, + # quit here after setting up enough information to mount it later. + if not lvd.format: + if not lvd.name: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="--noformat used without --name") + + dev = devicetree.getDeviceByName("%s-%s" % (vg.name, lvd.name)) + if not dev: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="No preexisting logical volume with the name \"%s\" was found." % lvd.name) + + dev.format.mountpoint = lvd.mountpoint + dev.format.mountopts = lvd.fsopts + self.handler.skipSteps.extend(["partition", "zfcpconfig", "parttype"]) + return lvd + + # Make sure this LV name is not already used in the requested VG. + tmp = devicetree.getDeviceByName("%s-%s" % (vg.name, lvd.name)) + if tmp: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="Logical volume name already used in volume group %s" % vg.name) + + # Size specification checks + if not lvd.preexist: + if lvd.percent == 0: + if lvd.size == 0: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="Size required") + elif not lvd.grow and lvd.size*1024 < vg.peSize: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="Logical volume size must be larger than the volume group physical extent size.") + elif lvd.percent <= 0 or lvd.percent > 100: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="Percentage must be between 0 and 100") + + # Now get a format to hold a lot of these extra values. + format = getFormat(type, + mountpoint=lvd.mountpoint, + mountopts=lvd.fsopts) + if not format: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="The \"%s\" filesystem type is not supported." % type) + + # If we were given a pre-existing LV to create a filesystem on, we need + # to verify it and its VG exists and then schedule a new format action + # to take place there. Also, we only support a subset of all the + # options on pre-existing LVs. + if lvd.preexist: + device = devicetree.getDeviceByName("%s-%s" % (vg.name, lvd.name)) + if not device: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent LV %s in logvol command" % lvd.name) + + devicetree.registerAction(ActionCreateFormat(device, format)) + else: + request = storage.newLV(format=format, + name=lvd.name, + vg=vg, + size=lvd.size, + grow=lvd.grow, + maxsize=lvd.maxSizeMB, + percent=lvd.percent) + + # FIXME: no way to specify an fsprofile right now + # if lvd.fsprofile: + # request.format.fsprofile = lvd.fsprofile + + storage.createDevice(request) if lvd.encrypted: - if lvd.passphrase and \ - not self.handler.anaconda.id.storage.encryptionPassphrase: - self.handler.anaconda.id.storage.encryptionPassphrase = lvd.passphrase - request.encryption = cryptodev.LUKSDevice(passphrase=lvd.passphrase, format=lvd.format) + if lvd.passphrase and not storage.encryptionPassphrase: + storage.encryptionPassphrase = lvd.passphrase - addPartRequest(self.handler.anaconda, request) - self.handler.skipSteps.extend(["partition", "zfcpconfig", "parttype"]) + luksformat = request.format + request.format = getFormat("luks", passphrase=lvd.passphrase, device=request.path) + luksdev = LUKSDevice("luks%d" % storage.nextID, + format=luksformat, + parents=request) + storage.createDevice(luksdev) + self.handler.skipSteps.extend(["partition", "zfcpconfig", "parttype"]) return lvd class Logging(commands.logging.FC6_Logging): -- cgit From b009de64c53f353b0e6cfe67c6d8a6524ddb636c Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 11 Mar 2009 16:09:59 -0400 Subject: Update the part command to work with the new storage code. --- kickstart.py | 181 ++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 97 insertions(+), 84 deletions(-) diff --git a/kickstart.py b/kickstart.py index c798a1d2c..748a5e8e9 100644 --- a/kickstart.py +++ b/kickstart.py @@ -565,11 +565,9 @@ class Partition(commands.partition.F9_Partition): def parse(self, args): pd = commands.partition.F9_Partition.parse(self, args) - uniqueID = None - - fsopts = "" - if pd.fsopts: - fsopts = pd.fsopts + storage = self.handler.id.storage + devicetree = storage.devicetree + kwargs = {} if pd.onbiosdisk != "": pd.disk = isys.doGetBiosDisk(pd.onbiosdisk) @@ -578,7 +576,7 @@ class Partition(commands.partition.F9_Partition): raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified BIOS disk %s cannot be determined" % pd.onbiosdisk) if pd.mountpoint == "swap": - filesystem = fileSystemTypeGet('swap') + type = "swap" pd.mountpoint = "" if pd.recommended: (pd.size, pd.maxSizeMB) = iutil.swapSuggestion() @@ -588,102 +586,117 @@ class Partition(commands.partition.F9_Partition): elif pd.mountpoint == "None": pd.mountpoint = "" if pd.fstype: - try: - filesystem = fileSystemTypeGet(pd.fstype) - except KeyError: - raise KickstartValueError, formatErrorMsg(self.lineno, msg="The \"%s\" filesystem type is not supported." % pd.fstype) + type = pd.fstype else: - filesystem = fileSystemTypeGetDefault() - elif pd.mountpoint == 'appleboot': - filesystem = fileSystemTypeGet("Apple Bootstrap") - pd.mountpoint = "" - elif pd.mountpoint == 'prepboot': - filesystem = fileSystemTypeGet("PPC PReP Boot") - pd.mountpoint = "" + type = storage.defaultFSType +# elif pd.mountpoint == 'appleboot': +# filesystem = fileSystemTypeGet("Apple Bootstrap") +# pd.mountpoint = "" +# elif pd.mountpoint == 'prepboot': +# filesystem = fileSystemTypeGet("PPC PReP Boot") +# pd.mountpoint = "" elif pd.mountpoint.startswith("raid."): - filesystem = fileSystemTypeGet("software RAID") - - if self.handler.ksRaidMapping.has_key(pd.mountpoint): - raise KickstartValueError, formatErrorMsg(self.lineno, msg="Defined RAID partition multiple times") - - # get a sort of hackish id - uniqueID = self.handler.ksID - self.handler.ksRaidMapping[pd.mountpoint] = uniqueID - self.handler.ksID += 1 + type = "mdmember" + kwargs["name"] = pd.mountpoint + + if devicetree.getDeviceByName(kwargs["name"]): + raise KickstartValueError, formatErrorMsg(self.lineno, msg="RAID partition defined multiple times") + pd.mountpoint = "" elif pd.mountpoint.startswith("pv."): - filesystem = fileSystemTypeGet("physical volume (LVM)") + type = "lvmpv" + kwargs["name"] = pd.mountpoint - if self.handler.ksPVMapping.has_key(pd.mountpoint): - raise KickstartValueError, formatErrorMsg(self.lineno, msg="Defined PV partition multiple times") + if devicetree.getDeviceByName(kwargs["name"]): + raise KickstartValueError, formatErrorMsg(self.lineno, msg="PV partition defined multiple times") - # get a sort of hackish id - uniqueID = self.handler.ksID - self.handler.ksPVMapping[pd.mountpoint] = uniqueID - self.handler.ksID += 1 pd.mountpoint = "" elif pd.mountpoint == "/boot/efi": - filesystem = fileSystemTypeGet("efi") - fsopts = "defaults,uid=0,gid=0,umask=0077,shortname=winnt" + type = "vfat" + pd.fsopts = "defaults,uid=0,gid=0,umask=0077,shortname=winnt" else: if pd.fstype != "": - try: - filesystem = fileSystemTypeGet(pd.fstype) - except KeyError: - raise KickstartValueError, formatErrorMsg(self.lineno, msg="The \"%s\" filesystem type is not supported." % pd.fstype) + type = pd.fstype else: - filesystem = fileSystemTypeGetDefault() + type = storage.defaultFSType - if pd.size is None and (pd.start == 0 and pd.end == 0) and pd.onPart == "": + # If this specified an existing request that we should not format, + # quit here after setting up enough information to mount it later. + if not pd.format: + if not pd.onPart: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="--noformat used without --onpart") + + dev = devicetree.getDeviceByName(pd.onPart) + if not dev: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="No preexisting partition with the name \"%s\" was found." % pd.onPart) + + dev.format.mountpoint = pd.mountpoint + dev.format.mountopts = pd.fsopts + self.handler.skipSteps.extend(["partition", "zfcpconfig", "parttype"]) + return pd + + # Size specification checks. + if pd.size is None and pd.onPart == "": raise KickstartValueError, formatErrorMsg(self.lineno, msg="Partition requires a size specification") - if pd.start != 0 and pd.disk == "": - raise KickstartValueError, formatErrorMsg(self.lineno, msg="Partition command with start cylinder requires a drive specification") - hds = map(lambda x: x.name, filter(lambda x: isys.mediaPresent(x.name), self.handler.id.storage.disks)) - if pd.disk not in hds and pd.disk in ('mapper/'+hd for hd in hds): - pd.disk = 'mapper/' + pd.disk - if pd.disk not in hds: - raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in partition command" % pd.disk) - - request = partRequests.PartitionSpec(filesystem, - mountpoint = pd.mountpoint, - format = pd.format, - fslabel = pd.label, - fsprofile = pd.fsprofile) - - if pd.size is not None: - request.size = pd.size - if pd.start != 0: - request.start = pd.start - if pd.end != 0: - request.end = pd.end - if pd.grow: - request.grow = pd.grow - if pd.maxSizeMB != 0: - request.maxSizeMB = pd.maxSizeMB - if pd.disk != "": - request.drive = [ pd.disk ] - if pd.primOnly: - request.primary = pd.primOnly - if uniqueID: - request.uniqueID = uniqueID - if pd.onPart != "": - request.device = pd.onPart - for areq in self.handler.id.storage.autoPartitionRequests: - if areq.device is not None and areq.device == pd.onPart: - raise KickstartValueError, formatErrorMsg(self.lineno, "Partition already used") - if fsopts != "": - request.fsopts = fsopts + # Now get a format to hold a lot of these extra values. + kwargs["format"] = getFormat(type, + mountpoint=pd.mountpoint, + label=pd.label, + mountopts=pd.fsopts) + if not kwargs["format"]: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="The \"%s\" filesystem type is not supported." % type) + + # If we were given a specific disk to create the partition on, verify + # that it exists first. If it doesn't exist, see if it exists with + # mapper/ on the front. If that doesn't exist either, it's an error. + if pd.disk: + disk = devicetree.getDeviceByName(pd.disk) + if not disk: + pd.disk = "mapper/" % pd.disk + disk = devicetree.getDeviceByName(pd.disk) + + if not disk: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in partition command" % pd.disk) + + kwargs["disks"] = [disk] + + kwargs["grow"] = pd.grow + kwargs["size"] = pd.size + kwargs["maxsize"] = pd.maxSizeMB + kwargs["primary"] = pd.primOnly + + # If we were given a pre-existing partition to create a filesystem on, + # we need to verify it exists and then schedule a new format action to + # take place there. Also, we only support a subset of all the options + # on pre-existing partitions. + if pd.onPart: + device = devicetree.getDeviceByName(pd.onPart) + if not device: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent partition %s in partition command" % pd.onPart) + + devicetree.registerAction(ActionCreateFormat(device, kwargs["format"])) + else: + request = storage.newPartition(**kwargs) + + # FIXME: no way to specify an fsprofile right now + # if pd.fsprofile: + # request.format.fsprofile = pd.fsprofile + + storage.createDevice(request) if pd.encrypted: - if pd.passphrase and \ - not self.handler.anaconda.id.storage.encryptionPassphrase: - self.handler.anaconda.id.storage.encryptionPassphrase = pd.passphrase - request.encryption = cryptodev.LUKSDevice(passphrase=pd.passphrase, format=pd.format) + if pd.passphrase and not storage.encryptionPassphrase: + storage.encryptionPassphrase = pd.passphrase - addPartRequest(self.handler.anaconda, request) - self.handler.skipSteps.extend(["partition", "zfcpconfig", "parttype"]) + luksformat = request.format + request.format = getFormat("luks", passphrase=pd.passphrase, device=request.path) + luksdev = LUKSDevice("luks%d" % storage.nextID, + format=luksformat, + parents=request) + storage.createDevice(luksdev) + self.handler.skipSteps.extend(["partition", "zfcpconfig", "parttype"]) return pd class Reboot(commands.reboot.FC6_Reboot): -- cgit From e8a7994647bc1ae0647b1d59f1160adbbab0f7b4 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 11 Mar 2009 16:10:30 -0400 Subject: Update the raid command to work with the new storage code. --- kickstart.py | 129 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 79 insertions(+), 50 deletions(-) diff --git a/kickstart.py b/kickstart.py index 748a5e8e9..fec811239 100644 --- a/kickstart.py +++ b/kickstart.py @@ -708,77 +708,106 @@ class Reboot(commands.reboot.FC6_Reboot): class Raid(commands.raid.F9_Raid): def parse(self, args): rd = commands.raid.F9_Raid.parse(self, args) + raidmems = [] - uniqueID = None + storage = self.handler.id.storage + devicetree = storage.devicetree + kwargs = {} if rd.mountpoint == "swap": - filesystem = fileSystemTypeGet('swap') + type = "swap" rd.mountpoint = "" elif rd.mountpoint.startswith("pv."): - filesystem = fileSystemTypeGet("physical volume (LVM)") + type = "lvmpv" + kwargs["name"] = rd.mountpoint - if self.handler.ksPVMapping.has_key(rd.mountpoint): - raise KickstartValueError, formatErrorMsg(self.lineno, msg="Defined PV partition multiple times") + if devicetree.getDeviceByName(kwargs["name"]): + raise KickstartValueError, formatErrorMsg(self.lineno, msg="PV partition defined multiple times") - # get a sort of hackish id - uniqueID = self.handler.ksID - self.handler.ksPVMapping[rd.mountpoint] = uniqueID - self.handler.ksID += 1 rd.mountpoint = "" else: if rd.fstype != "": - try: - filesystem = fileSystemTypeGet(rd.fstype) - except KeyError: - raise KickstartValueError, formatErrorMsg(self.lineno, msg="The \"%s\" filesystem type is not supported." % rd.fstype) + type = rd.fstype else: - filesystem = fileSystemTypeGetDefault() + type = storage.defaultFSType - # sanity check mountpoint + # Sanity check mountpoint if rd.mountpoint != "" and rd.mountpoint[0] != '/': raise KickstartValueError, formatErrorMsg(self.lineno, msg="The mount point is not valid.") - raidmems = [] + # If this specifies an existing request that we should not format, + # quit here after setting up enough information to mount it later. + if not rd.format: + if not rd.device: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="--noformat used without --device") + + dev = devicetree.getDeviceByName(rd.device) + if not dev: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="No preexisting RAID device with the name \"%s\" was found." % rd.device) + + dev.format.mountpoint = lvd.mountpoint + dev.format.mountopts = lvd.fsopts + self.handler.skipSteps.extend(["partition", "zfcpconfig", "parttype"]) + return rd - # get the unique ids of each of the raid members + # Get a list of all the RAID members. for member in rd.members: - if member not in self.handler.ksRaidMapping.keys(): + dev = devicetree.getDeviceByName(member) + if not dev: raise KickstartValueError, formatErrorMsg(self.lineno, msg="Tried to use undefined partition %s in RAID specification" % member) - if member in self.handler.ksUsedMembers: - raise KickstartValueError, formatErrorMsg(self.lineno, msg="Tried to use RAID member %s in two or more RAID specifications" % member) - - raidmems.append(self.handler.ksRaidMapping[member]) - self.handler.ksUsedMembers.append(member) - - if rd.level == "" and not rd.preexist: - raise KickstartValueError, formatErrorMsg(self.lineno, msg="RAID Partition defined without RAID level") - if len(raidmems) == 0 and not rd.preexist: - raise KickstartValueError, formatErrorMsg(self.lineno, msg="RAID Partition defined without any RAID members") - - request = partRequests.RaidRequestSpec(filesystem, - mountpoint = rd.mountpoint, - raidmembers = raidmems, - raidlevel = rd.level, - raidspares = rd.spares, - format = rd.format, - raidminor = rd.device, - preexist = rd.preexist, - fsprofile = rd.fsprofile) - - if uniqueID is not None: - request.uniqueID = uniqueID - if rd.preexist and rd.device != "": - request.device = "md%s" % rd.device - if rd.fsopts != "": - request.fsopts = rd.fsopts + + raidmems.append(dev) + + if not rd.preexist: + if len(raidmems) == 0: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="RAID Partition defined without any RAID members") + + if rd.level == "": + raise KickstartValueError, formatErrorMsg(self.lineno, msg="RAID Partition defined without RAID level") + + # Now get a format to hold a lot of these extra values. + kwargs["format"] = getFormat(type, + mountpoint=rd.mountpoint, + mountopts=rd.fsopts) + if not kwargs["format"]: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="The \"%s\" filesystem type is not supported." % type) + + kwargs["name"] = rd.device + kwargs["level"] = rd.level + kwargs["parents"] = raidmems + kwargs["memberDevices"] = len(raidmems) + kwargs["totalDevices"] = kwargs["memberDevices"]+rd.spares + + # If we were given a pre-existing RAID to create a filesystem on, + # we need to verify it exists and then schedule a new format action + # to take place there. Also, we only support a subset of all the + # options on pre-existing RAIDs. + if rd.preexist: + device = devicetree.getDeviceByName(rd.name) + if not device: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specifeid nonexisted RAID %s in raid command" % rd.name) + + devicetree.registerAction(ActionCreateFormat(device, kwargs["format"])) + else: + request = storage.newMDArray(**kwargs) + + # FIXME: no way to specify an fsprofile right now + # if pd.fsprofile: + # request.format.fsprofile = pd.fsprofile + + storage.createDevice(request) if rd.encrypted: - if rd.passphrase and \ - not self.handler.anaconda.id.storage.encryptionPassphrase: - self.handler.anaconda.id.storage.encryptionPassphrase = rd.passphrase - request.encryption = cryptodev.LUKSDevice(passphrase=rd.passphrase, format=rd.format) + if rd.passphrase and not storage.encryptionPassphrase: + storage.encryptionPassphrase = rd.passphrase + + luksformat = request.format + request.format = getFormat("luks", passphrase=rd.passphrase, device=request.path) + luksdev = LUKSDevice("luks%d" % storage.nextID, + format=luksformat, + parents=request) + storage.createDevice(luksdev) - addPartRequest(self.handler.anaconda, request) self.handler.skipSteps.extend(["partition", "zfcpconfig", "parttype"]) return rd -- cgit From a8f0fa0603962ca3b4b67e1e37f7a497b2deb3e3 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 11 Mar 2009 16:10:47 -0400 Subject: Update the volgroup command to work with the new storage code. --- kickstart.py | 53 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 37 insertions(+), 16 deletions(-) diff --git a/kickstart.py b/kickstart.py index fec811239..cc9999a70 100644 --- a/kickstart.py +++ b/kickstart.py @@ -864,30 +864,51 @@ class VolGroup(commands.volgroup.FC3_VolGroup): vgd = commands.volgroup.FC3_VolGroup.parse(self, args) pvs = [] - # get the unique ids of each of the physical volumes + storage = self.handler.id.storage + devicetree = storage.devicetree + + # Get a list of all the physical volume devices that make up this VG. for pv in vgd.physvols: - if pv not in self.handler.ksPVMapping.keys(): + dev = devicetree.getDeviceByName(pv) + if not dev: raise KickstartValueError, formatErrorMsg(self.lineno, msg="Tried to use undefined partition %s in Volume Group specification" % pv) - pvs.append(self.handler.ksPVMapping[pv]) + + pvs.append(dev) if len(pvs) == 0 and not vgd.preexist: raise KickstartValueError, formatErrorMsg(self.lineno, msg="Volume group defined without any physical volumes. Either specify physical volumes or use --useexisting.") - if vgd.pesize not in lvm.getPossiblePhysicalExtents(floor=1024): + if vgd.pesize not in getPossiblePhysicalExtents(floor=1024): raise KickstartValueError, formatErrorMsg(self.lineno, msg="Volume group specified invalid pesize") - # get a sort of hackish id - uniqueID = self.handler.ksID - self.handler.ksVGMapping[vgd.vgname] = uniqueID - self.handler.ksID += 1 - - request = partRequests.VolumeGroupRequestSpec(vgname = vgd.vgname, - physvols = pvs, - preexist = vgd.preexist, - format = vgd.format, - pesize = vgd.pesize) - request.uniqueID = uniqueID - addPartRequest(self.handler.anaconda, request) + # If --noformat was given, there's really nothing to do. + if not vgd.format: + if not vgd.name: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="--noformat used without giving a name") + + dev = devicetree.getDeviceByName(vgd.name) + if not dev: + raise KickstartValueError, formatErrorMsg(self.lineno, msg="No preexisting VG with the name \"%s\" was found." % vgd.name) + + return vgd + + # If we were given a pre-existing VG to use, we need to verify it + # exists and then schedule a new format action to take place there. + # Also, we only support a subset of all the options on pre-existing + # VGs. + if vgd.preexist: + device = devicetree.getDeviceByName(vgd.name) + if not device: + raise KicsktartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent VG %s in volgroup command" % vgd.name) + + devicetree.registerAction(ActionCreateFormat(device)) + else: + request = storage.newVG(pvs=pvs, + name=vgd.vgname, + peSize=vgd.pesize/1024.0) + + storage.createDevice(request) + return vgd class XConfig(commands.xconfig.F10_XConfig): -- cgit From 85b93e8407359c49ba1364b29fe2a67a236c010d Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 11 Mar 2009 16:11:01 -0400 Subject: Make sure the device has a diskType before attempting to check what it is. --- storage/devicetree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index 2a40fc423..109db1a0c 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -758,7 +758,7 @@ class DeviceTree(object): if isinstance(dep, PartitionDevice): # collect all of the logicals on the same disk for part in self.getDevicesByInstance(PartitionDevice): - if part.isLogical and part.disk == dep.disk: + if part.partType and part.isLogical and part.disk == dep.disk: logicals.append(part) for device in self.devices.values(): -- cgit From 89394bbfe2ff4eeed5bd168f6f8a935cd7fa8407 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 11 Mar 2009 17:01:58 -0400 Subject: Get rid of the mappings and ksID as well. --- kickstart.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/kickstart.py b/kickstart.py index cc9999a70..1000f8e0b 100644 --- a/kickstart.py +++ b/kickstart.py @@ -1013,13 +1013,6 @@ class AnacondaKSHandler(superclass): self.permanentSkipSteps = [] self.skipSteps = [] self.showSteps = [] - self.ksRaidMapping = {} - self.ksUsedMembers = [] - self.ksPVMapping = {} - self.ksVGMapping = {} - # XXX hack to give us a starting point for RAID, LVM, etc unique IDs. - self.ksID = 100000 - self.anaconda = anaconda self.id = self.anaconda.id -- cgit From 855f41c85087d4dcc1dd5fbdd85637168429a873 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 12 Mar 2009 10:14:55 -0400 Subject: It's clearPartDisks, not clearPartDrives. --- textw/partition_text.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/textw/partition_text.py b/textw/partition_text.py index 0419f8a38..41e1db5cc 100644 --- a/textw/partition_text.py +++ b/textw/partition_text.py @@ -92,7 +92,7 @@ class PartitionTypeWindow: # restore the drive list each time disks = anaconda.id.storage.disks - cleardrives = anaconda.id.storage.clearPartDrives + cleardrives = anaconda.id.storage.clearPartDisks for disk in disks: model = disk.partedDisk.device.model @@ -140,7 +140,7 @@ class PartitionTypeWindow: anaconda.dispatch.skipStep("autopartitionexecute", skip = 0) anaconda.id.storage.clearPartType = partmethod_ans - anaconda.id.storage.clearPartDrives = sel + anaconda.id.storage.clearPartDisks = sel break # ask to review autopartition layout - but only if it's not custom partitioning -- cgit From 2c6e9c9f2601c129c16e3f4336342bdd10fa3e3d Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 12 Mar 2009 10:19:13 -0400 Subject: currentSize is expected to be a float, so convert it to one (#489882). --- storage/formats/fs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 144e8fd43..063ca16cb 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -193,7 +193,7 @@ class FS(DeviceFormat): size = 0 if self.exists: size = self._size - return size + return float(size) def _getFormatArgs(self, options=None): argv = [] -- cgit From e4cd790b81531c9814a4e15374a9f49f73f0487d Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 11 Mar 2009 23:02:16 -0500 Subject: Honor the zerombr kickstart directive. --- storage/devicetree.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index 109db1a0c..49f551c34 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -972,7 +972,11 @@ class DeviceTree(object): device = self.getDeviceByName(name) if device is None: try: - cb=lambda:questionInitializeDisk(self.intf, name) + if self.zeroMbr: + cb = lambda: True + else: + cb = lambda: questionInitializeDisk(self.intf, name) + device = DiskDevice(name, major=udev_device_get_major(info), minor=udev_device_get_minor(info), @@ -1149,7 +1153,11 @@ class DeviceTree(object): # Create the DMRaidArray try: - cb=lambda:questionInitializeDisk(self.intf,rs.name) + if self.zeroMbr: + cb = lambda: True + else: + cb = lambda: questionInitializeDisk(self.intf, + rs.name) dm_array = DMRaidArrayDevice(rs.name, major=major, minor=minor, raidSet=rs, -- cgit From d1bacca5c6922532b85c1e6f6fceae45f1453e40 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 11 Mar 2009 23:18:09 -0500 Subject: Add udev rules for handling for mdraid arrays. This will enable us to detect filesystems or other formatting on md arrays. --- 70-anaconda.rules | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/70-anaconda.rules b/70-anaconda.rules index e379a22bd..363f7ae3b 100644 --- a/70-anaconda.rules +++ b/70-anaconda.rules @@ -1,7 +1,7 @@ ACTION!="add|change", GOTO="anaconda_end" SUBSYSTEM!="block", GOTO="anaconda_end" -KERNEL!="dm-*", GOTO="anaconda_raid_probe" +KERNEL!="dm-*", GOTO="anaconda_mdraid" IMPORT{program}="/usr/sbin/dmsetup info -c --nameprefixes --unquoted --rows --noheadings -o name,uuid,suspended,readonly,major,minor,open,tables_loaded -j%M -m%m" ENV{DM_NAME}!="?*", GOTO="anaconda_end" @@ -15,8 +15,32 @@ IMPORT{program}="vol_id --export $tempnode" OPTIONS="link_priority=-100" ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}" -LABEL="anaconda_raid_probe" +LABEL="anaconda_mdraid" +KERNEL!="md*", GOTO="anaconda_mdraid_member" + +# container devices have a metadata version of e.g. 'external:ddf' and +# never leave state 'inactive' +ATTR{md/metadata_version}=="external:[A-Za-z]*", ATTR{md/array_state}=="inactive", GOTO="md_ignore_state" +TEST!="md/array_state", GOTO="anaconda_mdraid_member" +ATTR{md/array_state}=="|clear|inactive", GOTO="anaconda_mdraid_member" +LABEL="md_ignore_state" + +IMPORT{program}="/usr/sbin/mdadm --detail --export $tempnode" +ENV{DEVTYPE}=="disk", ENV{MD_NAME}=="?*", SYMLINK+="disk/by-id/md-name-$env{MD_NAME}", OPTIONS+="string_escape=replace" +ENV{DEVTYPE}=="disk", ENV{MD_UUID}=="?*", SYMLINK+="disk/by-id/md-uuid-$env{MD_UUID}" +ENV{DEVTYPE}=="disk", ENV{MD_DEVNAME}=="?*", SYMLINK+="md/$env{MD_DEVNAME}" +ENV{DEVTYPE}=="partition", ENV{MD_NAME}=="?*", SYMLINK+="disk/by-id/md-name-$env{MD_NAME}-part%n", OPTIONS+="string_escape=replace" +ENV{DEVTYPE}=="partition", ENV{MD_UUID}=="?*", SYMLINK+="disk/by-id/md-uuid-$env{MD_UUID}-part%n" +ENV{DEVTYPE}=="partition", ENV{MD_DEVNAME}=="*[^0-9]", SYMLINK+="md/$env{MD_DEVNAME}%n" +ENV{DEVTYPE}=="partition", ENV{MD_DEVNAME}=="*[0-9]", SYMLINK+="md/$env{MD_DEVNAME}p%n" +IMPORT{program}="vol_id --export $tempnode" +OPTIONS+="link_priority=100" +OPTIONS+="watch" +ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}" +ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}" + +LABEL="anaconda_mdraid_member" # probe raid metadata of mdraid member devices ENV{ID_FS_TYPE}=="linux_raid_member", IMPORT{program}="/usr/sbin/mdadm --examine --export $tempnode" -- cgit From 6f5aaf8afa57ed73a6e20fbd7695129987fa4563 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 12 Mar 2009 10:49:48 -0500 Subject: Fix typo. --- storage/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/__init__.py b/storage/__init__.py index ac1ff4ec9..f0ed7d635 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1463,7 +1463,7 @@ class FSSet(object): (device.format.type != "swap" or swapoff): continue - device.teardownFormat() + device.format.teardown() device.teardown() self.active = False -- cgit From 5ddeb7dfb5ad4b85e000284bd2b7e2666b622e96 Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Thu, 12 Mar 2009 18:06:50 +0100 Subject: Fix typo. --- storage/partitioning.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index 66c0c29b3..e900d18a4 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -201,7 +201,7 @@ def doAutoPartition(anaconda): sys.exit(0) if anaconda.id.storage.doAutoPart: - _scheduleLVs(anaconda, dev) + _scheduleLVs(anaconda, devs) # sanity check the collection of devices log.warning("not sanity checking storage config because I don't know how yet") -- cgit From 4021d1148f710a0e10d5eefe72d353477a245697 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 12 Mar 2009 12:39:27 -0500 Subject: Call storage.exceptionDisks, not diskset.exceptionDisks. (#489615) --- text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text.py b/text.py index b91504056..8d6c6e563 100644 --- a/text.py +++ b/text.py @@ -242,7 +242,7 @@ class SaveExceptionWindow: toplevel.add(scpGrid, 0, 5, (0, 0, 0, 1)) toplevel.add(buttons, 0, 6, growx=1) - dests = self.anaconda.id.diskset.exceptionDisks(self.anaconda) + dests = self.anaconda.id.storage.exceptionDisks() if len(dests) > 0: for (dev, desc) in dests: -- cgit From a650068892310da459f5d8a7c126ce86a56d9ee5 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 12 Mar 2009 10:29:16 -0400 Subject: Do not write "Running..." to stdout, as that could be tty1. Doing so sends a whole lot of spew to tty1 which not only looks bad, but makes VNC and text installs look even worse than they already are. Also, let's write output from execWithPulseProgress to the program.log too. --- iutil.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/iutil.py b/iutil.py index d9e8a776a..221504f5c 100644 --- a/iutil.py +++ b/iutil.py @@ -80,7 +80,6 @@ def execWithRedirect(command, argv, stdin = None, stdout = None, runningLog = open("/tmp/program.log", "a") runningLog.write("Running... %s\n" % ([command] + argv,)) - os.write(stdout, "Running... %s\n" %([command] + argv,)) try: proc = subprocess.Popen([command] + argv, stdin=stdin, @@ -197,7 +196,8 @@ def execWithPulseProgress(command, argv, stdin = None, stdout = None, elif stderr is None or not isinstance(stderr, file): stderr = sys.stderr.fileno() - os.write(stdout, "Running... %s\n" %([command] + argv,)) + runningLog = open("/tmp/program.log", "a") + runningLog.write("Running... %s\n" % ([command] + argv,)) p = os.pipe() childpid = os.fork() @@ -224,6 +224,7 @@ def execWithPulseProgress(command, argv, stdin = None, stdout = None, raise IOError, args os.write(stdout, s) + runningLog.write(s) if progress: progress.pulse() if len(s) < 1: -- cgit From ffa9248f7281ad437aec8e62eaf7c68d28b46c30 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 12 Mar 2009 14:01:24 -0400 Subject: A getter doesn't usually take a parameter (#489965). --- storage/formats/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py index 66e34fb31..3eaeb0fd7 100644 --- a/storage/formats/__init__.py +++ b/storage/formats/__init__.py @@ -173,7 +173,7 @@ class DeviceFormat(object): def _setOptions(self, options): self._options = options - def _getOptions(self, options): + def _getOptions(self): return self._options options = property(_getOptions, _setOptions) -- cgit From 9f1c7c9b16a5dbffb8f12f56ba50cbf818069044 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 12 Mar 2009 15:33:50 -0400 Subject: Don't create a PartitionDevice for devices that do not exist (#489122). This is a strange situation to be in, but there appears to be a corner case where parted cannot read the disk label despite there being a partition table on the disk. anaconda will prompt to create a new disk label. This means there's no partitions on the disk, but the list of partitions to probe still has the old partition list. So, we should just ignore devices that no longer exist. --- storage/devices.py | 7 +++---- storage/devicetree.py | 4 ++++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index ae1ad0918..13d85b8a0 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -173,9 +173,7 @@ def PartitionDeviceFactory(*args, **kwargs): parents = kwargs["parents"] if isinstance(parents, Device): parents = [parents] - # we receive a list of parents. look for the disk that contains the name - # if we dont find the name we will return PartitionDevice and let it raise - # the exception. + # we receive a list of parents. look for the disk that contains the name. for root in roots: for parent in parents: path = os.path.join(root,norm_name) @@ -194,7 +192,8 @@ def PartitionDeviceFactory(*args, **kwargs): return PartitionDevice(*args, **kwargs) # If we get here, we did not find anything. - return PartitionDevice(*args, **kwargs) + log.warning("unable to find parted device for %s" % norm_name) + return None class Device(object): """ A generic device. diff --git a/storage/devicetree.py b/storage/devicetree.py index 49f551c34..15da568be 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -880,6 +880,8 @@ class DeviceTree(object): minor=udev_device_get_minor(info), \ exists=True, \ parents=[disk]) + if not device: + return self._addDevice(device) #self.ignoredDisks.append(name) @@ -1012,6 +1014,8 @@ class DeviceTree(object): minor=udev_device_get_minor(info), exists=True, parents=[disk]) + if not device: + return self._addDevice(device) # -- cgit From 159526f02651142201e4db6bacd52fe924f0b63f Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 12 Mar 2009 23:12:37 -0500 Subject: New version: 11.5.0.29 --- anaconda.spec | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index dccc87822..b37389f9d 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.28 +Version: 11.5.0.29 Release: 1 License: GPLv2+ Group: Applications/System @@ -209,6 +209,37 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Thu Mar 12 2009 David Lehman - 11.5.0.29-1 +- Don't create a PartitionDevice for devices that do not exist (#489122). + (clumens) +- A getter doesn't usually take a parameter (#489965). (clumens) +- Do not write "Running..." to stdout, as that could be tty1. (clumens) +- Call storage.exceptionDisks, not diskset.exceptionDisks. (#489615) + (dlehman) +- Fix typo. (jgranado) +- Fix typo. (dlehman) +- Add udev rules for handling for mdraid arrays. (dlehman) +- Honor the zerombr kickstart directive. (dlehman) +- currentSize is expected to be a float, so convert it to one (#489882). + (clumens) +- It's clearPartDisks, not clearPartDrives. (clumens) +- Get rid of the mappings and ksID as well. (clumens) +- Make sure the device has a diskType before attempting to check what it is. + (clumens) +- Update the volgroup command to work with the new storage code. (clumens) +- Update the raid command to work with the new storage code. (clumens) +- Update the part command to work with the new storage code. (clumens) +- Update the logvol command to work with the new storage code. (clumens) +- addPartRequest is no longer needed. (clumens) +- Don't set default partitioning in every kickstart case. (clumens) +- Clear partitions before scheduling requests. (clumens) +- Always go through doAutoPart. (clumens) +- Format modules import fix (mgracik) +- Fixed the format modules import (mgracik) +- Allow overriding the anaconda udev rules from an updates.img (hdegoede) +- If a pv somehow does not contain a vg_name, do not try to get other vg + info (hdegoede) + * Wed Mar 11 2009 David Cantrell - 11.5.0.28-1 - Fix a few bugs in the lvm dialog. (#489022) (dlehman) - Modify livecd.py to work with new storage backend. (dlehman) -- cgit From aad95f552d7090557bfc25d8eec29a10284024e6 Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Tue, 10 Mar 2009 15:58:00 +0100 Subject: Add a list that lvm should ignore. We use the --config argument from lvm to pass a list of devices from devicetree.ignoredDisks to lvm so those disks can be ignored in lvm commands. * storage/devicelibs/lvm.py (config_args): Add global variable that will contain the "--config" argument. The argument will be a list ["--config", STRING_ARGS] * storage/devicelibs/lvm.py (composeConfig): Add function to create the config_args argument. * storage/devicelibs/lvm.py (lvm_cc_addFilterRejectRegexp): New function to regenerate the config_args with a new regular expression. * storage/devicelibs/lvm.py (pv*, lv*, vg*): Use the global variable for each LVM command. * storage/devicetree.py (DeviceTree): Instead of doing a self.ignoredDisk.append(DISK), we create a new function that adds DISK to the devicetree list and to the lvm list and whatever else we need. --- storage/devicelibs/lvm.py | 186 ++++++++++++++++++++++++++++++++++------------ storage/devicetree.py | 24 +++--- 2 files changed, 155 insertions(+), 55 deletions(-) diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py index 0faee10e8..0a1971152 100644 --- a/storage/devicelibs/lvm.py +++ b/storage/devicelibs/lvm.py @@ -49,6 +49,56 @@ def has_lvm(): return has_lvm +# Start config_args handling code +# +# Theoretically we can handle all that can be handled with the LVM --config +# argument. For every time we call an lvm_cc (lvm compose config) funciton +# we regenerate the config_args with all global info. +config_args = [] # Holds the final argument list +config_args_data = { "filterRejects": [], # regular expressions to reject. + "filterAccepts": [] } # regexp to accept + +def _composeConfig(): + """lvm command accepts lvm.conf type arguments preceded by --config. """ + global config_args, config_args_data + config_args = [] + + filter_string = "" + rejects = config_args_data["filterRejects"] + # we don't need the accept for now. + # accepts = config_args_data["filterAccepts"] + # if len(accepts) > 0: + # for i in range(len(rejects)): + # filter_string = filter_string + ("\"a|%s|\", " % accpets[i]) + + if len(rejects) > 0: + for i in range(len(rejects)): + filter_string = filter_string + ("\"r|%s|\", " % rejects[i]) + + + filter_string = " filter=[%s] " % filter_string.strip(",") + + # As we add config strings we should check them all. + if filter_string == "": + # Nothing was really done. + return + + # devices_string can have (inside the brackets) "dir", "scan", + # "preferred_names", "filter", "cache_dir", "write_cache_state", + # "types", "sysfs_scan", "md_component_detection". see man lvm.conf. + devices_string = " devices { %s } " % (filter_string) # strings can be added + config_string = devices_string # more strings can be added. + config_args = ["--config", config_string] + +def lvm_cc_addFilterRejectRegexp(regexp): + """ Add a regular expression to the --config string.""" + global config_args_data + config_args_data["filterRejects"].append(regexp) + + # compoes config once more. + _composeConfig() +# End config_args handling code. + 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. @@ -108,8 +158,11 @@ def clampSize(size, pesize, roundup=None): return long(round(float(size)/float(pesize)) * pesize) def pvcreate(device): - rc = iutil.execWithRedirect("lvm", - ["pvcreate", device], + args = ["pvcreate"] + \ + config_args + \ + [device] + + rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -117,11 +170,12 @@ def pvcreate(device): raise LVMError("pvcreate failed for %s" % device) def pvresize(device, size): - size_arg = "%dm" % size - rc = iutil.execWithRedirect("lvm", - ["pvresize", - "--setphysicalvolumesize", size_arg, - device], + args = ["pvresize"] + \ + ["--setphysicalvolumesize", ("%dm" % size)] + \ + config_args + \ + [device] + + rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -129,8 +183,11 @@ def pvresize(device, size): raise LVMError("pvresize failed for %s" % device) def pvremove(device): - rc = iutil.execWithRedirect("lvm", - ["pvremove", device], + args = ["pvremove"] + \ + config_args + \ + [device] + + rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -147,12 +204,13 @@ def pvinfo(device): 'devices { scan = "/dev" filter = ["a/loop0/", "r/.*/"] }' """ #cfg = "'devices { scan = \"/dev\" filter = [\"a/%s/\", \"r/.*/\"] }'" - rc = iutil.execWithCapture("lvm", - ["pvs", "--noheadings", - "--units", "m", - "-o", - "pv_name,pv_mda_count,vg_name,vg_uuid", - device], + args = ["pvs", "--noheadings"] + \ + ["--units", "m"] + \ + ["-o", "pv_name,pv_mda_count,vg_name,vg_uuid"] + \ + config_args + \ + [device] + + rc = iutil.execWithCapture("lvm", args, stderr = "/dev/tty5") vals = rc.split() if not vals: @@ -175,10 +233,11 @@ def vgcreate(vg_name, pv_list, pe_size): argv = ["vgcreate"] if pe_size: argv.extend(["-s", "%dM" % pe_size]) + argv.extend(config_args) argv.append(vg_name) argv.extend(pv_list) - rc = iutil.execWithRedirect("lvm", - argv, + + rc = iutil.execWithRedirect("lvm", argv, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -187,7 +246,11 @@ def vgcreate(vg_name, pv_list, pe_size): raise LVMError("vgcreate failed for %s" % vg_name) def vgremove(vg_name): - rc = iutil.execWithRedirect("lvm", ["vgremove", vg_name], + args = ["vgremove"] + \ + config_args +\ + [vg_name] + + rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -196,7 +259,11 @@ def vgremove(vg_name): raise LVMError("vgremove failed for %s" % vg_name) def vgactivate(vg_name): - rc = iutil.execWithRedirect("lvm", ["vgchange", "-a", "y", vg_name], + args = ["vgchange", "-a", "y"] + \ + config_args + \ + [vg_name] + + rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -204,7 +271,11 @@ def vgactivate(vg_name): raise LVMError("vgactivate failed for %s" % vg_name) def vgdeactivate(vg_name): - rc = iutil.execWithRedirect("lvm", ["vgchange", "-a", "n", vg_name], + args = ["vgchange", "-a", "n"] + \ + config_args + \ + [vg_name] + + rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -213,7 +284,12 @@ def vgdeactivate(vg_name): raise LVMError("vgdeactivate failed for %s" % vg_name) def vgreduce(vg_name, pv_list): - rc = iutil.execWithRedirect("lvm", ["vgreduce", vg_name] + pv_list, + args = ["vgreduce"] + \ + config_args + \ + [vg_name] + \ + pv_list + + rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -222,11 +298,15 @@ def vgreduce(vg_name, pv_list): raise LVMError("vgreduce failed for %s" % vg_name) def vginfo(vg_name): + args = ["vgs", "--noheadings", "--nosuffix"] + \ + ["--units", "m"] + \ + ["-o", "uuid,size,free,extent_size,extent_count,free_count,pv_count"] + \ + config_args + \ + [vg_name] + buf = iutil.execWithCapture("lvm", - ["vgs", "--noheadings", "--nosuffix", "--units", "m", "-o", - "uuid,size,free,extent_size,extent_count,free_count,pv_count", - vg_name], - stderr="/dev/tty5") + args, + stderr="/dev/tty5") info = buf.split() if len(info) != 7: raise LVMError(_("vginfo failed for %s" % vg_name)) @@ -237,10 +317,14 @@ def vginfo(vg_name): return d def lvs(vg_name): + args = ["lvs", "--noheadings", "--nosuffix"] + \ + ["--units", "m"] + \ + ["-o", "lv_name,lv_uuid,lv_size"] + \ + config_args + \ + [vg_name] + buf = iutil.execWithCapture("lvm", - ["lvs", "--noheadings", "--nosuffix", - "--units", "m", "-o", - "lv_name,lv_uuid,lv_size", vg_name], + args, stderr="/dev/tty5") lvs = {} @@ -258,12 +342,13 @@ def lvs(vg_name): return lvs def lvcreate(vg_name, lv_name, size): - size_arg = "%dm" % size - rc = iutil.execWithRedirect("lvm", - ["lvcreate", - "-L", size_arg, - "-n", lv_name, - vg_name], + args = ["lvcreate"] + \ + ["-L", "%dm" % size] + \ + ["-n", lv_name] + \ + config_args + \ + [vg_name] + + rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -272,8 +357,11 @@ def lvcreate(vg_name, lv_name, size): raise LVMError("lvcreate failed for %s/%s" % (vg_name, lv_name)) def lvremove(vg_name, lv_name): - lv_path = "%s/%s" % (vg_name, lv_name) - rc = iutil.execWithRedirect("lvm", ["lvremove", lv_path], + args = ["lvremove"] + \ + config_args + \ + ["%s/%s" % (vg_name, lv_name)] + + rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -282,12 +370,12 @@ def lvremove(vg_name, lv_name): raise LVMError("lvremove failed for %s" % lv_path) def lvresize(vg_name, lv_name, size): - lv_path = "%s/%s" % (vg_name, lv_name) - size_arg = "%dm" % size - rc = iutil.execWithRedirect("lvm", - ["lvresize", - "-L", size_arg, - lv_path], + args = ["lvresize"] + \ + ["-L", "%dm" % size] + \ + config_args + \ + ["%s/%s" % (vg_name, lv_name)] + + rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -297,8 +385,11 @@ def lvresize(vg_name, lv_name, size): def lvactivate(vg_name, lv_name): # see if lvchange accepts paths of the form 'mapper/$vg-$lv' - lv_path = "%s/%s" % (vg_name, lv_name) - rc = iutil.execWithRedirect("lvm", ["lvchange", "-a", "y", lv_path], + args = ["lvchange", "-a", "y"] + \ + config_args + \ + ["%s/%s" % (vg_name, lv_name)] + + rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) @@ -306,8 +397,11 @@ def lvactivate(vg_name, lv_name): raise LVMError("lvactivate failed for %s" % lv_path) def lvdeactivate(vg_name, lv_name): - lv_path = "%s/%s" % (vg_name, lv_name) - rc = iutil.execWithRedirect("lvm", ["lvchange", "-a", "n", lv_path], + args = ["lvchange", "-a", "n"] + \ + config_args + \ + ["%s/%s" % (vg_name, lv_name)] + + rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", stderr = "/dev/tty5", searchPath=1) diff --git a/storage/devicetree.py b/storage/devicetree.py index 15da568be..fb33eff3e 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -162,13 +162,19 @@ class DeviceTree(object): self._actions = [] self.intf = intf - self.ignoredDisks = ignored self.exclusiveDisks = exclusive self.zeroMbr = zeroMbr self.__passphrase = passphrase self.__luksDevs = {} if luksDict and isinstance(luksDict, dict): self.__luksDevs = luksDict + self._ignoredDisks = [] + for disk in ignored: + self.addIgnoredDisk(disk) + + def addIgnoredDisk(self, disk): + self._ignoredDisks.append(disk) + lvm.lvm_cc_addFilterRejectRegexp(disk) def pruneActions(self): """ Prune loops and redundant actions from the queue. """ @@ -790,10 +796,10 @@ class DeviceTree(object): if not sysfs_path: return None - if name in self.ignoredDisks: + if name in self._ignoredDisks: return True - for ignored in self.ignoredDisks: + for ignored in self._ignoredDisks: if ignored == os.path.basename(os.path.dirname(sysfs_path)): # this is a partition on a disk in the ignore list return True @@ -883,7 +889,7 @@ class DeviceTree(object): if not device: return self._addDevice(device) - #self.ignoredDisks.append(name) + #self.addIgnoredDisk(name) # if we get here, we found all of the slave devices and # something must be wrong -- if all of the slaves are in @@ -986,7 +992,7 @@ class DeviceTree(object): initcb=cb) self._addDevice(device) except DeviceUserDeniedFormatError: #drive not initialized? - self.ignoredDisks.append(name) + self.addIgnoredDisk(name) elif udev_device_is_partition(info): log.debug("%s is a partition" % name) device = self.getDeviceByName(name) @@ -1138,12 +1144,12 @@ class DeviceTree(object): if rs is None: # we ignore the device in the hope that all the devices # from this set will be ignored. - self.ignoredDisks.append(device.name) + self.addIgnoredDisk(device.name) return elif rs.name in self.ignoredDisks: # If the rs is being ignored, we should ignore device too. - self.ignoredDisks.append(device.name) + self.addIgnoredDisk(device.name) return else: @@ -1179,8 +1185,8 @@ class DeviceTree(object): # major=major, minor=minor, uuid=uuid, name=name) except DeviceUserDeniedFormatError: # We should ignore the dmriad and its components - self.ignoredDisks.append(rs.name) - self.ignoredDisks.append(device.name) + self.addIgnoredDisk(rs.name) + self.addIgnoredDisk(device.name) rs.deactivate() elif format.type == "lvmpv": # lookup/create the VG and LVs -- cgit From 0096bedad0748366ca2bdfdec77c46b82ae7e68f Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Thu, 12 Mar 2009 16:55:17 +0100 Subject: Fix editing of existing logical volume. Also (probably harmless) typo corrected. --- iw/lvm_dialog_gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index ad081b204..316266513 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -888,7 +888,7 @@ class VolumeGroupEditor: if self.vg: origvname = self.vg.name else: - origname = None + origvname = None if origvname != volname: # maybe we should see if _any_ device has this name @@ -908,8 +908,8 @@ class VolumeGroupEditor: # pvs, pesize are taken care of in widget callbacks # (clickCB, peChangeCB) - self.vg.name = volname if self.isNew: + self.vg.name = volname self.actions.insert(0, ActionCreateDevice(self.vg)) return self.actions -- cgit From 09e79bc538c870fc50d227780d04555561050447 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Tue, 10 Mar 2009 14:47:19 +0100 Subject: Add created user to default group created for the user. Documentation for ks option user says (--groups option) that user is added to default group, but we've been only setting the default group as user's primary group. --- users.py | 1 + 1 file changed, 1 insertion(+) diff --git a/users.py b/users.py index 3f5d73538..4d2e1a5dd 100644 --- a/users.py +++ b/users.py @@ -129,6 +129,7 @@ class Users: self.admin.lockUser(userEnt) # Add the user to all the groups they should be part of. + grpLst.append(self.admin.lookupGroupByName(name)) for grp in grpLst: grp.add(libuser.MEMBERNAME, name) self.admin.modifyGroup(grp) -- cgit From e9c01eb4e23874e3294d52eabc5673f82e07999f Mon Sep 17 00:00:00 2001 From: Martin Gracik Date: Mon, 9 Mar 2009 10:59:19 +0100 Subject: Added test case for devicelib mdraid.py. Rewrote the devicelibs unittest baseclass so we can create and remove another loop devices within the tests. Rewrote the isRaidXY(raidlevel) functions in mdraid.py into one function isRaid(XY, raidlevel). Rewrote the get_raid_min_members and get_raid_max_spares functions to use the new isRaid function, and not use many if-elif-else statements. Changed the minimum raid members for raid10 to 2. --- storage/devicelibs/mdraid.py | 78 +++++++++++++------------ tests/storage/devicelibs/baseclass.py | 60 ++++++++++--------- tests/storage/devicelibs/mdraid.py | 105 ++++++++++++++++++++++++++++++++++ 3 files changed, 178 insertions(+), 65 deletions(-) create mode 100644 tests/storage/devicelibs/mdraid.py diff --git a/storage/devicelibs/mdraid.py b/storage/devicelibs/mdraid.py index a0d2f6ae3..a1a40d2e1 100644 --- a/storage/devicelibs/mdraid.py +++ b/storage/devicelibs/mdraid.py @@ -28,6 +28,13 @@ from ..errors import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) +# raidlevels constants +RAID10 = 10 +RAID6 = 6 +RAID5 = 5 +RAID1 = 1 +RAID0 = 0 + def getRaidLevels(): avail = [] try: @@ -52,51 +59,46 @@ def getRaidLevels(): raid_levels = getRaidLevels() -# FIXME: these functions should be consolidated into one function -def isRaid10(raidlevel): - """Return whether raidlevel is a valid descriptor of RAID10.""" - return raidlevel in ("RAID10", "10", 10) - -def isRaid6(raidlevel): - """Return whether raidlevel is a valid descriptor of RAID6.""" - return raidlevel in ("RAID6", "6", 6) - -def isRaid5(raidlevel): - """Return whether raidlevel is a valid descriptor of RAID5.""" - return raidlevel in ("RAID5", "5", 5) +def isRaid(raid, raidlevel): + """Return whether raidlevel is a valid descriptor of raid""" + raid_descriptors = {RAID10: ("RAID10", "10", 10), + RAID6: ("RAID6", "6", 6), + RAID5: ("RAID5", "5", 5), + RAID1: ("mirror", "RAID1", "1", 1), + RAID0: ("stripe", "RAID0", "0", 0)} -def isRaid1(raidlevel): - """Return whether raidlevel is a valid descriptor of RAID1.""" - return raidlevel in ("mirror", "RAID1", "1", 1) - -def isRaid0(raidlevel): - """Return whether raidlevel is a valid descriptor of RAID0.""" - return raidlevel in ("stripe", "RAID0", "0", 0) + if raid in raid_descriptors: + return raidlevel in raid_descriptors[raid] + else: + raise ValueError, "invalid raid level %d" % raid 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" + raid_min_members = {RAID10: 2, + RAID6: 4, + RAID5: 3, + RAID1: 2, + RAID0: 2} + + for raid, min_members in raid_min_members.items(): + if isRaid(raid, raidlevel): + return min_members + + raise ValueError, "invalid raid level %d" % raidlevel 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" + raid_max_spares = {RAID10: lambda: max(0, nummembers - get_raid_min_members(RAID10)), + RAID6: lambda: max(0, nummembers - get_raid_min_members(RAID6)), + RAID5: lambda: max(0, nummembers - get_raid_min_members(RAID5)), + RAID1: lambda: max(0, nummembers - get_raid_min_members(RAID1)), + RAID0: lambda: 0} + + for raid, max_spares_func in raid_max_spares.items(): + if isRaid(raid, raidlevel): + return max_spares_func() + + raise ValueError, "invalid raid level %d" % raidlevel def mdcreate(device, level, disks, spares=0): argv = ["--create", device, "--level", str(level)] diff --git a/tests/storage/devicelibs/baseclass.py b/tests/storage/devicelibs/baseclass.py index efc9c803f..01fe6c779 100644 --- a/tests/storage/devicelibs/baseclass.py +++ b/tests/storage/devicelibs/baseclass.py @@ -15,33 +15,39 @@ class TestDevicelibs(unittest.TestCase): def setUp(self): for dev, file in self._LOOP_DEVICES: - proc = subprocess.Popen(["dd", "if=/dev/zero", "of=%s" % file, "bs=1024", "count=102400"]) - while True: - proc.communicate() - if proc.returncode is not None: - rc = proc.returncode - break - if rc: - raise OSError, "dd failed creating the file %s" % file - - proc = subprocess.Popen(["losetup", dev, file]) - while True: - proc.communicate() - if proc.returncode is not None: - rc = proc.returncode - break - if rc: - raise OSError, "losetup failed setting up the loop device %s" % dev + self.makeLoopDev(dev, file) def tearDown(self): for dev, file in self._LOOP_DEVICES: - proc = subprocess.Popen(["losetup", "-d", dev]) - while True: - proc.communicate() - if proc.returncode is not None: - rc = proc.returncode - break - if rc: - raise OSError, "losetup failed removing the loop device %s" % dev - - os.remove(file) + self.removeLoopDev(dev, file) + + def makeLoopDev(self, device_name, file_name): + proc = subprocess.Popen(["dd", "if=/dev/zero", "of=%s" % file_name, "bs=1024", "count=102400"]) + while True: + proc.communicate() + if proc.returncode is not None: + rc = proc.returncode + break + if rc: + raise OSError, "dd failed creating the file %s" % file_name + + proc = subprocess.Popen(["losetup", device_name, file_name]) + while True: + proc.communicate() + if proc.returncode is not None: + rc = proc.returncode + break + if rc: + raise OSError, "losetup failed setting up the loop device %s" % device_name + + def removeLoopDev(self, device_name, file_name): + proc = subprocess.Popen(["losetup", "-d", device_name]) + while True: + proc.communicate() + if proc.returncode is not None: + rc = proc.returncode + break + if rc: + raise OSError, "losetup failed removing the loop device %s" % device_name + + os.remove(file_name) diff --git a/tests/storage/devicelibs/mdraid.py b/tests/storage/devicelibs/mdraid.py new file mode 100644 index 000000000..6e49e55b0 --- /dev/null +++ b/tests/storage/devicelibs/mdraid.py @@ -0,0 +1,105 @@ +import baseclass +import unittest +import storage.devicelibs.mdraid as mdraid + +import time + +class TestMDRaid(baseclass.TestDevicelibs): + + def testMDRaidStuff(self): + ## + ## getRaidLevels + ## + # pass + self.assertEqual(mdraid.getRaidLevels(), mdraid.getRaidLevels()) + + ## + ## get_raid_min_members + ## + # pass + self.assertEqual(mdraid.get_raid_min_members(mdraid.RAID0), 2) + self.assertEqual(mdraid.get_raid_min_members(mdraid.RAID1), 2) + self.assertEqual(mdraid.get_raid_min_members(mdraid.RAID5), 3) + self.assertEqual(mdraid.get_raid_min_members(mdraid.RAID6), 4) + self.assertEqual(mdraid.get_raid_min_members(mdraid.RAID10), 2) + + # fail + # unsupported raid + self.assertRaises(ValueError, mdraid.get_raid_min_members, 4) + + ## + ## get_raid_max_spares + ## + # pass + self.assertEqual(mdraid.get_raid_max_spares(mdraid.RAID0, 5), 0) + self.assertEqual(mdraid.get_raid_max_spares(mdraid.RAID1, 5), 3) + self.assertEqual(mdraid.get_raid_max_spares(mdraid.RAID5, 5), 2) + self.assertEqual(mdraid.get_raid_max_spares(mdraid.RAID6, 5), 1) + self.assertEqual(mdraid.get_raid_max_spares(mdraid.RAID10, 5), 3) + + # fail + # unsupported raid + self.assertRaises(ValueError, mdraid.get_raid_max_spares, 4, 5) + + ## + ## mdcreate + ## + # pass + self.assertEqual(mdraid.mdcreate("/dev/md0", 1, [self._LOOP_DEV0, self._LOOP_DEV1]), None) + # wait for raid to settle + time.sleep(2) + + # fail + self.assertRaises(mdraid.MDRaidError, mdraid.mdcreate, "/dev/md1", 1, ["/not/existing/dev0", "/not/existing/dev1"]) + + ## + ## mddeactivate + ## + # pass + self.assertEqual(mdraid.mddeactivate("/dev/md0"), None) + + # fail + self.assertRaises(mdraid.MDRaidError, mdraid.mddeactivate, "/not/existing/md") + + ## + ## mdadd + ## + # pass + # TODO + + # fail + self.assertRaises(mdraid.MDRaidError, mdraid.mdadd, "/not/existing/device") + + ## + ## mdactivate + ## + # pass + self.assertEqual(mdraid.mdactivate("/dev/md0", [self._LOOP_DEV0, self._LOOP_DEV1], super_minor=0), None) + # wait for raid to settle + time.sleep(2) + + # fail + self.assertRaises(mdraid.MDRaidError, mdraid.mdactivate, "/not/existing/md", super_minor=1) + # requires super_minor or uuid + self.assertRaises(ValueError, mdraid.mdactivate, "/dev/md1") + + ## + ## mddestroy + ## + # pass + # deactivate first + self.assertEqual(mdraid.mddeactivate("/dev/md0"), None) + + self.assertEqual(mdraid.mddestroy(self._LOOP_DEV0), None) + self.assertEqual(mdraid.mddestroy(self._LOOP_DEV1), None) + + # fail + # not a component + self.assertRaises(mdraid.MDRaidError, mdraid.mddestroy, "/dev/md0") + self.assertRaises(mdraid.MDRaidError, mdraid.mddestroy, "/not/existing/device") + + +if __name__ == "__main__": + suite = unittest.TestLoader().loadTestsFromTestCase(TestMDRaid) + unittest.TextTestRunner(verbosity=2).run(suite) + -- cgit From 6ab09cd1dc61378e018dcef98ae48bdabf04e880 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 12 Mar 2009 17:38:02 -0400 Subject: Only select the Core group in text mode (#488754). This is consistent with the graphical installation mode, which will only install Core if all groups are deselected. --- textw/task_text.py | 1 - 1 file changed, 1 deletion(-) diff --git a/textw/task_text.py b/textw/task_text.py index 94afce651..94d417c2d 100644 --- a/textw/task_text.py +++ b/textw/task_text.py @@ -27,6 +27,5 @@ class TaskWindow: anaconda.backend.resetPackageSelections() anaconda.backend.selectGroup("Core") - anaconda.backend.selectGroup("Base") return INSTALL_OK -- cgit From 96af8446516d5de40808de9f2fec0575088248e4 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Fri, 13 Mar 2009 13:58:03 +0100 Subject: Fix getting of number of total devices of sw raid. The bug manifests itself in edit raid ui dialog of existing raid array. * storage/devices.py: For existing partitions use parents to get number of total devices in array, for new partitions use value from device instance which is initialized when the instance is created * iw.raid_dialog_gui.py: Initialize number of total devices when creating raid array in UI. --- iw/raid_dialog_gui.py | 1 + storage/devices.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index d45b582ed..099a9c965 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -246,6 +246,7 @@ class RaidEditor: level=level, format=format, parents=raidmembers, + totalDevices=len(raidmembers), memberDevices=members) actions.append(ActionCreateDevice(request)) actions.append(ActionCreateFormat(request)) diff --git a/storage/devices.py b/storage/devices.py index 13d85b8a0..f72d39d3f 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -1867,7 +1867,7 @@ class MDRaidArrayDevice(StorageDevice): def totalDevices(self): """ Total number of devices in the array, including spares. """ count = len(self.parents) - if self.exists: + if not self.exists: count = self._totalDevices return count -- cgit From 37f6448719b73cb398ee4dc463bca237867f0bfa Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 12 Mar 2009 18:56:09 -0500 Subject: Use the correct keyword for luks map names ('name', not 'mapName'). --- storage/devicetree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index fb33eff3e..df5a6eddb 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1039,7 +1039,7 @@ class DeviceTree(object): if format_type == "crypto_LUKS": # luks/dmcrypt - kwargs["mapName"] = "luks-%s" % uuid + kwargs["name"] = "luks-%s" % uuid elif format_type == "linux_raid_member": # mdraid try: -- cgit From 64d856def83048916ac32ca9184e0fae2ae2aaa9 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 12 Mar 2009 18:59:36 -0500 Subject: Default to a name based on the uuid for existing luks mappings. This is what we default to in the device tree, so this just lines the two up. --- storage/formats/luks.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/storage/formats/luks.py b/storage/formats/luks.py index 349f034f8..f5c2363f7 100644 --- a/storage/formats/luks.py +++ b/storage/formats/luks.py @@ -70,7 +70,9 @@ class LUKS(DeviceFormat): self.__passphrase = kwargs.get("passphrase") self._key_file = kwargs.get("key_file") - if not self.mapName and self.device: + if not self.mapName and self.exists and self.uuid: + self.mapName = "luks-%s" % self.uuid + elif not self.mapName and self.device: self.mapName = "luks-%s" % os.path.basename(self.device) def _setPassphrase(self, passphrase): -- cgit From 76c46ee886a3f96d963bc1175c76f6e8be731e00 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 12 Mar 2009 21:41:58 -0500 Subject: Fix infinite loops in partition screen populate. (#490051) --- iw/partition_gui.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index d58e13989..7b22aabd1 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -817,6 +817,7 @@ class PartitionWindow(InstallWindow): # ignore the tiny < 1 MB partitions (#119479) if part.getSize(unit="MB") <= 1.0: if not part.active or not device.bootable: + part = part.nextPartition() continue stripe.add(part) @@ -863,6 +864,7 @@ class PartitionWindow(InstallWindow): else: self.tree.appendToHiddenPartitionsList(part) self.tree.remove(iter) + part = part.nextPartition() continue else: self.tree[iter]['Mount Point'] = "" -- cgit From d2cfbef7084141b90dc0373678e7735adbc3ab2b Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 12 Mar 2009 22:26:25 -0500 Subject: Add a deep copy method to Device since we can't just use copy.deepcopy. The various parted objects cannot be copied using copy.deepcopy, so we make shallow copies of those and deep copies of all other attributes. --- storage/devices.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/storage/devices.py b/storage/devices.py index f72d39d3f..59090f66e 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -92,8 +92,10 @@ """ + import os import math +import copy # device backend modules from devicelibs import mdraid @@ -256,6 +258,24 @@ class Device(object): for parent in self.parents: parent.addChild() + def __deepcopy__(self, memo): + """ Create a deep copy of a Device instance. + + We can't do copy.deepcopy on parted objects, which is okay. + For these parted objects, we just do a shallow copy. + """ + new = self.__class__.__new__(self.__class__) + memo[id(self)] = new + shallow_copy_attrs = ('partedDisk', 'partedDevice', + '_partedPartition', '_origPartedDisk') + for (attr, value) in self.__dict__.items(): + if attr in shallow_copy_attrs: + setattr(new, attr, copy.copy(value)) + else: + setattr(new, attr, copy.deepcopy(value, memo)) + + return new + def removeChild(self): log_method_call(self, name=self.name, kids=self.kids) self.kids -= 1 -- cgit From 84a421e18af68009ca2d3cd6fb1ecfeb6042def8 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 12 Mar 2009 22:28:21 -0500 Subject: Save a copy of the device stack so we can destroy the format. (#489975) Since everything is a reference, we are subject to any changes that may be made to the device/format stack occupied by "our" format from the time the action is created to the time it is executed. If devices lower in the stack have their formats scheduled for destruction, we will be unable to setup the device on which "our" format exists (because the ActionDestroyFormat constructor unsets the device's format attribute so the tree can reflect the future state of things). So, we save a deep copy and are no longer subject to changes initiated from outside our sphere of interest. --- storage/deviceaction.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/storage/deviceaction.py b/storage/deviceaction.py index 7d6e8367f..f9a73229d 100644 --- a/storage/deviceaction.py +++ b/storage/deviceaction.py @@ -21,6 +21,7 @@ # Red Hat Author(s): Dave Lehman # +import copy from parted import PARTITION_BOOT from udev import udev_settle @@ -143,7 +144,7 @@ class DeviceAction(object): if not isinstance(device, StorageDevice): raise ValueError("arg 1 must be a StorageDevice instance") self.device = device - #self._backup = deepcopy(device) + def execute(self, intf=None): """ perform the action """ @@ -296,7 +297,12 @@ class ActionDestroyFormat(DeviceAction): def __init__(self, device): DeviceAction.__init__(self, device) - self.origFormat = device.format + # Save a deep copy of the device stack this format occupies. + # This is necessary since the stack of devices and formats + # required to get to this format may get yanked out from under + # us between now and execute. + self._device = copy.deepcopy(device) + self.origFormat = self._device.format if device.format.exists: device.format.teardown() self.device.format = None @@ -309,13 +315,16 @@ class ActionDestroyFormat(DeviceAction): # unset partition flags and commit self.device.unsetFlag(self.origFormat.partedFlag) self.device.disk.commit() - udev_settle() - self.device.setup() + # set up our copy of the original device stack since the + # reference we got may have had any number of things changed + # since then (most notably, formats removed by this very + # class' constructor) + self._device.setup() self.origFormat.destroy() udev_settle() - self.device.teardown() + self._device.teardown() def cancel(self): self.device.format = self.origFormat -- cgit From bc58cf1af4644d67d0ecc1fd6333563f8675f362 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 12 Mar 2009 23:01:38 -0500 Subject: Wait til everyone knows the format/fs is no longer active. The cryptsetup call to close the luks mapping fails with some regularity, presumably because the device is still claiming to be busy. The addition of this call to udev_settle has seemed to solve the problem in my testing. --- storage/devices.py | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/devices.py b/storage/devices.py index 59090f66e..39578c01b 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -1310,6 +1310,7 @@ class LUKSDevice(DMCryptDevice): if self.status and self.format.exists: self.format.teardown() + udev_settle() if self.slave.format.exists: self.slave.format.teardown() -- cgit From 3ca330121f748d608e583458c6882f3df807cc26 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 13 Mar 2009 00:45:58 -0500 Subject: Don't clear partitions containing the install media. --- storage/partitioning.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/storage/partitioning.py b/storage/partitioning.py index e900d18a4..cf151d1da 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -268,6 +268,10 @@ def clearPartitions(storage): part.disk.name not in storage.clearPartDisks: continue + # don't clear partitions holding install media + if part.name in storage.protectedPartitions: + continue + # we don't want to fool with extended partitions, freespace, &c if part.partType not in (parted.PARTITION_NORMAL, parted.PARTITION_LOGICAL): -- cgit From 466244ac808660d4ccc33c818d9b171b65a00ef7 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 13 Mar 2009 11:02:46 -0400 Subject: Rename /etc/modprobe.d/anaconda to /etc/modprobe.d/anaconda.conf The kernel tells me that all module config files have to end with .conf in the future or they'll be ignored. We had better rename it, then. --- loader/modules.c | 6 +++--- yuminstall.py | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/loader/modules.c b/loader/modules.c index 705a3ebeb..bddda2b29 100644 --- a/loader/modules.c +++ b/loader/modules.c @@ -101,7 +101,7 @@ void mlAddBlacklist(char *module) { blacklists = realloc(blacklists, sizeof(*blacklists) * (numblacklists + 1)); blacklists[numblacklists] = strdup(module); numblacklists++; - writeModulesConf("/etc/modprobe.d/anaconda"); + writeModulesConf("/etc/modprobe.d/anaconda.conf"); } static void addOption(const char *module, const char *option) { @@ -225,7 +225,7 @@ static int doLoadModule(const char *module, char ** args) { for (i = 0; args[i] ; i++) { addOption(module, args[i]); } - writeModulesConf("/etc/modprobe.d/anaconda"); + writeModulesConf("/etc/modprobe.d/anaconda.conf"); } rc = execv("/sbin/modprobe", argv); _exit(rc); @@ -252,7 +252,7 @@ void mlRemoveBlacklist(char *module) { void mlInitModuleConfig() { readModuleOpts(); readBlacklist(); - writeModulesConf("/etc/modprobe.d/anaconda"); + writeModulesConf("/etc/modprobe.d/anaconda.conf"); } /* load a module with a given list of arguments */ diff --git a/yuminstall.py b/yuminstall.py index dd272bf3c..af6963de0 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -1492,9 +1492,9 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon if not upgrade: anaconda.id.storage.fsset.write(anaconda.rootPath) # rootpath mode doesn't have this file around - if os.access("/etc/modprobe.d/anaconda", os.R_OK): - shutil.copyfile("/etc/modprobe.d/anaconda", - anaconda.rootPath + "/etc/modprobe.d/anaconda") + if os.access("/etc/modprobe.d/anaconda".conf, os.R_OK): + shutil.copyfile("/etc/modprobe.d/anaconda.conf", + anaconda.rootPath + "/etc/modprobe.d/anaconda.conf") anaconda.id.network.write(instPath=anaconda.rootPath, anaconda=anaconda) anaconda.id.storage.write(anaconda.rootPath) if not anaconda.id.isHeadless: -- cgit From 4f7ad077b523a0dbb1ace6339720f875dec6574f Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 13 Mar 2009 13:13:30 -0400 Subject: Use the right import path for checkbootloader (#490049). --- iw/upgrade_bootloader_gui.py | 2 +- textw/upgrade_bootloader_text.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/iw/upgrade_bootloader_gui.py b/iw/upgrade_bootloader_gui.py index 57cfc63b3..08f2403df 100644 --- a/iw/upgrade_bootloader_gui.py +++ b/iw/upgrade_bootloader_gui.py @@ -23,7 +23,7 @@ from iw_gui import * import gtk -import checkbootloader +from booty import checkbootloader from constants import * import gettext diff --git a/textw/upgrade_bootloader_text.py b/textw/upgrade_bootloader_text.py index f863efbb1..ab45075f8 100644 --- a/textw/upgrade_bootloader_text.py +++ b/textw/upgrade_bootloader_text.py @@ -23,7 +23,7 @@ from snack import * from constants_text import * from flags import flags import string -import checkbootloader +from booty import checkbootloader from constants import * import gettext -- cgit From 12c4fb21429e61fc0dedbd8e3fd2a2f0d3e3eeac Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 13 Mar 2009 13:34:13 -0400 Subject: Add mediaPresent and eject to the OpticalDevice class. These no longer belong in isys.py as putting them in the classes is much more correct. I also put a mediaPresent method on Device in general. This will come in handy when we start dealing with USB CF readers and similar devices that can be present without having media. --- anaconda | 2 +- booty/bootloaderInfo.py | 2 +- installmethod.py | 6 +++--- isys/isys.py | 33 --------------------------------- kickstart.py | 2 +- storage/devices.py | 30 ++++++++++++++++++++++++++++++ yuminstall.py | 3 ++- 7 files changed, 38 insertions(+), 40 deletions(-) diff --git a/anaconda b/anaconda index dc50999f6..a4ed591e0 100755 --- a/anaconda +++ b/anaconda @@ -1026,7 +1026,7 @@ if __name__ == "__main__": continue log.info("attempting to eject %s" % drive.path) - isys.ejectCdrom(drive.path) + drive.eject() del anaconda.intf diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 2fdf16ab7..d1147348c 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -453,7 +453,7 @@ class bootloaderInfo: f.write("\n") def updateDriveList(self, sortedList=[]): - self._drivelist = map(lambda x: x.name, filter(lambda x: isys.mediaPresent(x.name), self.storage.disks)) + self._drivelist = map(lambda x: x.name, filter(lambda dev: dev.mediaPresent, self.storage.disks)) self._drivelist.sort(isys.compareDrives) # If we're given a sort order, make sure the drives listed in it diff --git a/installmethod.py b/installmethod.py index f2c3d7191..c412c944a 100644 --- a/installmethod.py +++ b/installmethod.py @@ -34,17 +34,17 @@ def doMethodComplete(anaconda): return None if anaconda.mediaDevice: - return anaconda.mediaDevice + return anaconda.id.storage.devicetree.getDeviceByName(anaconda.mediaDevice) # If we booted off the boot.iso instead of disc 1, eject that as well. if anaconda.stage2 and anaconda.stage2.startswith("cdrom://"): dev = anaconda.stage2[8:].split(':')[0] - return dev + return anaconda.id.storage.devicetree.getDeviceByName(dev) anaconda.backend.complete(anaconda) dev = _ejectDevice() if dev: - isys.ejectCdrom(dev) + dev.eject() mtab = "/dev/root / ext3 ro 0 0\n" rootDevice = anaconda.id.storage.fsset.rootDevice diff --git a/isys/isys.py b/isys/isys.py index fb2fa5d57..07c69a0a1 100755 --- a/isys/isys.py +++ b/isys/isys.py @@ -437,22 +437,6 @@ def ext2HasJournal(device): hasjournal = _isys.e2hasjournal(device); return hasjournal -def ejectCdrom(device): - # XXX this should go into storage.devices.OpticalDevice - if not os.path.exists(device): - device = "/dev/%s" % device - - fd = os.open(device, os.O_RDONLY|os.O_NONBLOCK) - - # this is a best effort - try: - _isys.ejectcdrom(fd) - except SystemError, e: - log.warning("error ejecting cdrom (%s): %s" %(device, e)) - pass - - os.close(fd) - def driveUsesModule(device, modules): """Returns true if a drive is using a prticular module. Only works for SCSI devices right now.""" @@ -481,23 +465,6 @@ def driveUsesModule(device, modules): pass return rc -## Check if a removable media drive (CD, USB key, etc.) has media present. -# @param device The basename of the device node. -# @return True if media is present in device, False otherwise. -def mediaPresent(device): - # XXX this should go into storage.devices.RemovableDevice or similar - try: - fd = os.open("/dev/%s" % device, os.O_RDONLY) - except OSError, (errno, strerror): - # error 123 = No medium found - if errno == 123: - return False - else: - return True - else: - os.close(fd) - return True - def driveIsIscsi(device): # ewww. just ewww. if not os.path.islink("/sys/block/%s/device" %(device,)): diff --git a/kickstart.py b/kickstart.py index 1000f8e0b..f24d6178a 100644 --- a/kickstart.py +++ b/kickstart.py @@ -238,7 +238,7 @@ class ClearPart(commands.clearpart.FC3_ClearPart): if self.type is None: self.type = CLEARPART_TYPE_NONE - hds = map(lambda x: x.name, filter(lambda x: isys.mediaPresent(x.name), self.handler.id.storage.disks)) + hds = map(lambda x: x.name, filter(lambda dev: dev.mediaPresent, self.handler.id.storage.disks)) for disk in self.drives: if disk not in hds: raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in clearpart command" % disk) diff --git a/storage/devices.py b/storage/devices.py index 39578c01b..283eb055c 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -381,6 +381,10 @@ class Device(object): return packages + @property + def mediaPresent(self): + return True + class NetworkDevice(Device): """ A network device """ @@ -2500,6 +2504,7 @@ class OpticalDevice(StorageDevice): major=major, minor=minor, exists=True, parents=parents, sysfsPath=sysfsPath) + @property def mediaPresent(self): """ Return a boolean indicating whether or not the device contains media. @@ -2508,12 +2513,37 @@ class OpticalDevice(StorageDevice): if not self.exists: raise DeviceError("device has not been created") + try: + fd = os.open(self.path, os.O_RDONLY) + except OSError as e: + # errno 123 = No medium found + if e.errno == 123: + return False + else: + return True + else: + os.close(fd) + return True + def eject(self): """ Eject the drawer. """ + import _isys + log_method_call(self, self.name, status=self.status) if not self.exists: raise DeviceError("device has not been created") + # Make a best effort attempt to do the eject. If it fails, it's not + # critical. + fd = os.open(self.path, os.O_RDONLY | os.O_NONBLOCK) + + try: + _isys.ejectcdrom(fd) + except SystemError as e: + log.warning("error ejecting cdrom %s: %s" % (device, e)) + + os.close(fd) + class ZFCPDiskDevice(DiskDevice): """ A mainframe ZFCP disk. """ diff --git a/yuminstall.py b/yuminstall.py index af6963de0..dc541b571 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -342,7 +342,8 @@ class AnacondaYum(YumSorter): unmountCD(self.tree, self.anaconda.intf.messageWindow) self.currentMedia = None - isys.ejectCdrom(self.anaconda.mediaDevice) + dev = self.anaconda.id.storage.devicetree.getDeviceByName(self.anaconda.mediaDevice) + dev.eject() while True: if self.anaconda.intf: -- cgit From 7531edf4c208d1f807fbe629575848cfade5c927 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 13 Mar 2009 14:31:14 -0400 Subject: Add __str__ methods to Device objects. --- storage/devices.py | 88 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) diff --git a/storage/devices.py b/storage/devices.py index 283eb055c..b3f7d2917 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -276,6 +276,16 @@ class Device(object): return new + def __str__(self): + s = ("%(type)s instance (%(id)s) --\n" + " description = %(descr)s name = %(name)s status = %(status)s" + " parents = %(parents)s\n" + " kids = %(kids)s\n" % + {"type": self.__class__.__name__, "id": "%#x" % id(self), + "name": self.name, "parents": self.parents, "kids": self.kids, + "descr": self.description, "status": self.status}) + return s + def removeChild(self): log_method_call(self, name=self.name, kids=self.kids) self.kids -= 1 @@ -465,6 +475,20 @@ class StorageDevice(Device): self.fstabComment = "" self.targetSize = self._size + def __str__(self): + s = Device.__str__(self) + s += (" uuid = %(uuid)s format = %(format)r size = %(size)s\n" + " major = %(major)s minor = %(minor)r exists = %(exists)s\n" + " sysfs path = %(sysfs)s label = %(diskLabel)s\n" + " target size = %(targetSize)s path = %(path)s\n" + " format args = %(formatArgs)s" % + {"uuid": self.uuid, "format": self.format, "size": self.size, + "major": self.major, "minor": self.minor, "exists": self.exists, + "sysfs": self.sysfsPath, "diskLabel": self.diskLabel, + "targetSize": self.targetSize, "path": self.path, + "formatArgs": self.formatArgs}) + return s + @property def path(self): """ Device node representing this device. """ @@ -713,6 +737,14 @@ class DiskDevice(StorageDevice): self.probe() + def __str__(self): + s = StorageDevice.__str__(self) + s += (" removable = %(removable)s partedDevice = %(partedDevice)r\n" + " partedDisk = %(partedDisk)r" % + {"removable": self.removable, "partedDisk": self.partedDisk, + "partedDevice": self.partedDevice}) + return s + @property def size(self): """ The disk's size in MB """ @@ -893,6 +925,17 @@ class PartitionDevice(StorageDevice): # req_base_size will always remain constant self.req_base_size = self._size + def __str__(self): + s = StorageDevice.__str__(self) + s += (" grow = %(grow)s max size = %(maxsize)s bootable = %(bootable)s\n" + " part type = %(partType)s primary = %(primary)s\n" + " partedPartition = %(partedPart)r disk = %(disk)r" % + {"grow": self.req_grow, "maxsize": self.req_max_size, + "bootable": self.bootable, "partType": self.partType, + "primary": self.req_primary, + "partedPart": self.partedPartition, "disk": self.disk}) + return s + @property def partType(self): """ Get the partition's type (as parted constant). """ @@ -1180,6 +1223,12 @@ class DMDevice(StorageDevice): self.target = target self.dmUuid = dmUuid + def __str__(self): + s = StorageDevice.__str__(self) + s += (" target = %(target)s dmUuid = %(dmUuid)s" % + {"target": self.target, "dmUuid": self.dmUuid}) + return s + def probe(self): """ Probe for any missing information about this device. @@ -1399,6 +1448,22 @@ class LVMVolumeGroupDevice(DMDevice): #self.probe() + def __str__(self): + s = DMDevice.__str__(self) + s += (" free = %(free)s PE Size = %(peSize)s PE Count = %(peCount)s\n" + " PE Free = %(peFree)s PV Count = %(pvCount)s\n" + " LV Names = %(lvNames)s modified = %(modified)s\n" + " extents = %(extents)s free space = %(freeSpace)s\n" + " free extents = %(freeExtents)s\n" + " PVs = %(pvs)s\n" + " LVs = %(lvs)s" % + {"free": self.free, "peSize": self.peSize, "peCount": self.peCount, + "peFree": self.peFree, "pvCount": self.pvCount, + "lvNames": self.lvNames, "modified": self.isModified, + "extents": self.extents, "freeSpace": self.freeSpace, + "freeExtents": self.freeExtents, "pvs": self.pvs, "lvs": self.lvs}) + return s + def probe(self): """ Probe for any information about this device. """ log_method_call(self, self.name, status=self.status) @@ -1721,6 +1786,12 @@ class LVMLogicalVolumeDevice(DMDevice): # here we go with the circular references self.vg._addLogVol(self) + def __str__(self): + s = DMDevice.__str__(self) + s += (" VG device = %(vgdev)r percent = %(percent)s" % + {"vgdev": self.vg, "percent": self.req_percent}) + return s + def probe(self): """ Probe for any missing information about this device. @@ -1879,6 +1950,15 @@ class MDRaidArrayDevice(StorageDevice): size = device.size return size + def __str__(self): + s = StorageDevice.__str__(self) + s += (" level = %(level)s bitmap = %(bitmap)s spares = %(spares)s\n" + " members = %(memberDevices)s\n" + " total devices = %(totalDevices)s" % + {"level": self.level, "bitmap": self.bitmap, "spares": self.spares, + "memberDevices": self.memberDevices, "totalDevices": self.totalDevices}) + return s + @property def mdadmConfEntry(self): """ This array's mdadm.conf entry. """ @@ -2485,6 +2565,8 @@ class iScsiDiskDevice(DiskDevice): DiskDevice.__init__(self, name, size=size, major=major, minor=minor, exists=exists, parents=parents, sysfsPath=sysfsPath) + def __str__(self): + return "FIXME: Please add iScsiDiskDevice.__str__" def probe(self): """ Probe for any missing information about this device. """ @@ -2560,6 +2642,12 @@ class ZFCPDiskDevice(DiskDevice): major=major, minor=minor, parents=parents, sysfsPath=sysfsPath) + def __str__(self): + s = DiskDevice.__str__(self) + s += (" devnum = %(devnum)s wwpn = %(wwpn)s fcplun = %(fcplun)s" % + {"devnum": self.devnum, "wwpn": self.wwpn, "fcplun": self.fcplun}) + return s + def probe(self): """ Probe for any missing information about this device. -- cgit From cd33f1451475dab853bc0021704257e5b1ac6879 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 13 Mar 2009 13:43:01 -0500 Subject: Make sure we return something other than None for new requests. --- storage/devices.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index b3f7d2917..fffebca84 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -166,11 +166,20 @@ def PartitionDeviceFactory(*args, **kwargs): norm_name = args[0].split("/")[-1] # We look for the disk in /dev. From PartitionDevice its the [0] one. - if (not kwargs.has_key("parents")) or\ - (kwargs.has_key("exists") and not kwargs["exists"]): + if not kwargs.get("exists") and not kwargs.get("parents"): # Cant really choose a good type of class, default to PartitionDevice # This will be considered as a request. return PartitionDevice(*args, **kwargs) + elif not kwargs.get("exists"): + # some requests have a disk specified + parents = kwargs["parents"] + if isinstance(parents, Device): + parents = [parents] + + if isinstance(parents[0], DMRaidArrayDevice): + return DMRaidPartitionDevice(*args, **kwargs) + else: + return PartitionDevice(*args, **kwargs) else: parents = kwargs["parents"] if isinstance(parents, Device): -- cgit From 7e6abe78159c8d7719fb2dacf6c59ffb9bd12b3a Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 13 Mar 2009 14:09:48 -0500 Subject: Schedule format create action for newly encrypted preexisting LV. --- iw/lvm_dialog_gui.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index 316266513..6aa5ef3b0 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -554,7 +554,7 @@ class VolumeGroupEditor: if _lv.format.type == "luks": # XXX this won't work if the lvs haven't been # added to the tree yet - _usedev = self.tree.getChildren(_lv)[0] + _usedev = self.findLUKSDev(_lv) _format = _usedev.format else: _usedev = _lv @@ -685,6 +685,7 @@ class VolumeGroupEditor: actions.append(ActionCreateFormat(usedev, format)) if luksdev: actions.append(ActionCreateDevice(luksdev)) + actions.append(ActionCreateFormat(luksdev)) if migrate: actions.append(ActionMigrateFormat(usedev)) -- cgit From fa67c6c414d1065c9bc1b626129c80bee81720c1 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 13 Mar 2009 14:15:25 -0500 Subject: Don't traceback if vg.teardown fails in recursive teardown. It's possible that another lv in the same vg either has a filesystem mounted or an active luks mapping, so just carry on. --- storage/devices.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/storage/devices.py b/storage/devices.py index fffebca84..39ffcf74d 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -1860,7 +1860,13 @@ class LVMLogicalVolumeDevice(DMDevice): lvm.lvdeactivate(self.vg.name, self._name) if recursive: - self.vg.teardown(recursive=recursive) + # It's likely that teardown of a VG will fail due to other + # LVs being active (filesystems mounted, &c), so don't let + # it bring everything down. + try: + self.vg.teardown(recursive=recursive) + except Exception as e: + log.debug("vg %s teardown failed; continuing" % self.vg.name) def create(self, intf=None): """ Create the device. """ -- cgit From ce5cafb47133419e690352111b26025cbf4e1e47 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 13 Mar 2009 17:00:54 -0500 Subject: Schedule format create for newly encrypted preexisting partition. This is analogous to the earlier fix for lvs. --- iw/partition_dialog_gui.py | 1 + 1 file changed, 1 insertion(+) diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index f4ebdcaaf..53d26f0b4 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -211,6 +211,7 @@ class PartitionEditor: actions.append(ActionCreateFormat(request, format)) if luksdev: actions.append(ActionCreateDevice(luksdev)) + actions.append(ActionCreateFormat(luksdev)) elif request.format.mountable: request.format.mountpoint = mountpoint -- cgit From ee8d802ad90bd9feffd2f6d3d735b7249495b1d9 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 13 Mar 2009 17:01:56 -0500 Subject: Access private attribute for luks dict. We don't want this attribute to have a getter because then it would appear in tracebacks, dumps, &c. --- storage/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/__init__.py b/storage/__init__.py index f0ed7d635..c786b63ca 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -206,7 +206,7 @@ class Storage(object): # save passphrases for luks devices so we don't have to reprompt for device in self.devices: if device.format.type == "luks" and device.format.exists: - self.__luksDevs[device.format.uuid] = device.format.__passphrase + self.__luksDevs[device.format.uuid] = device.format._LUKS__passphrase w = self.anaconda.intf.waitWindow(_("Finding Devices"), _("Finding storage devices...")) -- cgit From f763452ebadc59df61455b7a5b01724af1b77653 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 13 Mar 2009 17:04:02 -0500 Subject: Fix supportable attribute for cmdline-enabled fstypes. --- storage/formats/fs.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 063ca16cb..f46785221 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -550,11 +550,9 @@ class FS(DeviceFormat): def migrationTarget(self): return self._migrationTarget - def supported(self): + @property + def utilsAvailable(self): # we aren't checking for fsck because we shouldn't need it - if not self._supported: - return False - for prog in [self.mkfsProg, self.resizefsProg, self.labelfsProg]: if not prog: continue @@ -565,6 +563,10 @@ class FS(DeviceFormat): return True + def supported(self): + log_method_call(self, supported=self._supported) + return self._supported and self.utilsAvailable + @property def mountable(self): return self.type in kernel_filesystems @@ -810,7 +812,7 @@ class BTRFS(FS): """ Is this filesystem a supported type? """ supported = self._supported if flags.cmdline.has_key("icantbelieveitsnotbtr"): - supported = FS.supported(self) + supported = self.utilsAvailable return supported @@ -834,7 +836,7 @@ class GFS2(FS): """ Is this filesystem a supported type? """ supported = self._supported if flags.cmdline.has_key("gfs2"): - supported = FS.supported(self) + supported = self.utilsAvailable return supported @@ -861,7 +863,7 @@ class JFS(FS): """ Is this filesystem a supported type? """ supported = self._supported if flags.cmdline.has_key("jfs"): - supported = FS.supported(self) + supported = self.utilsAvailable return supported -- cgit From 888aaf0671db8815c61c8f0c5b9c11d4ae551690 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 13 Mar 2009 18:37:38 -0500 Subject: New version: 11.5.0.30 --- anaconda.spec | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index b37389f9d..603fcc234 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.29 +Version: 11.5.0.30 Release: 1 License: GPLv2+ Group: Applications/System @@ -209,6 +209,34 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Fri Mar 13 2009 David Lehman - 11.5.0.30-1 +- Fix supportable attribute for cmdline-enabled fstypes. (dlehman) +- Access private attribute for luks dict. (dlehman) +- Schedule format create for newly encrypted preexisting partition. (dlehman) +- Don't traceback if vg.teardown fails in recursive teardown. (dlehman) +- Schedule format create action for newly encrypted preexisting LV. (dlehman) +- Make sure we return something other than None for new requests. (dlehman) +- Add __str__ methods to Device objects. (clumens) +- Add mediaPresent and eject to the OpticalDevice class. (clumens) +- Use the right import path for checkbootloader (#490049). (clumens) +- Rename /etc/modprobe.d/anaconda to /etc/modprobe.d/anaconda.conf (clumens) +- Don't clear partitions containing the install media. (dlehman) +- Wait til everyone knows the format/fs is no longer active. (dlehman) +- Save a copy of the device stack so we can destroy the format. (#489975) + (dlehman) +- Add a deep copy method to Device since we can't just use copy.deepcopy. + (dlehman) +- Fix infinite loops in partition screen populate. (#490051) (dlehman) +- Default to a name based on the uuid for existing luks mappings. (dlehman) +- Use the correct keyword for luks map names ('name', not 'mapName'). + (dlehman) +- Fix getting of number of total devices of sw raid. (rvykydal) +- Only select the Core group in text mode (#488754). (clumens) +- Added test case for devicelib mdraid.py. (mgracik) +- Add created user to default group created for the user. (rvykydal) +- Fix editing of existing logical volume. (rvykydal) +- Add a list that lvm should ignore. (jgranado) + * Thu Mar 12 2009 David Lehman - 11.5.0.29-1 - Don't create a PartitionDevice for devices that do not exist (#489122). (clumens) -- cgit From 35f3ac5bc5930456d700a09407a5f89f06429d4e Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 13 Mar 2009 19:29:41 -0500 Subject: Make paths somewhat flexible so we'll work in normal environments. This is needed for live installs since they run on a normal system. --- 70-anaconda.rules | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/70-anaconda.rules b/70-anaconda.rules index 363f7ae3b..374b63a3d 100644 --- a/70-anaconda.rules +++ b/70-anaconda.rules @@ -1,9 +1,12 @@ ACTION!="add|change", GOTO="anaconda_end" SUBSYSTEM!="block", GOTO="anaconda_end" +ENV{ANACBIN}="/sbin" +TEST!="$env{ANACBIN}/dmsetup", ENV{ANACBIN}="/usr/sbin" + KERNEL!="dm-*", GOTO="anaconda_mdraid" -IMPORT{program}="/usr/sbin/dmsetup info -c --nameprefixes --unquoted --rows --noheadings -o name,uuid,suspended,readonly,major,minor,open,tables_loaded -j%M -m%m" +IMPORT{program}="$env{ANACBIN}/dmsetup info -c --nameprefixes --unquoted --rows --noheadings -o name,uuid,suspended,readonly,major,minor,open,tables_loaded -j%M -m%m" ENV{DM_NAME}!="?*", GOTO="anaconda_end" SYMLINK+="disk/by-id/dm-name-$env{DM_NAME}" @@ -25,7 +28,7 @@ TEST!="md/array_state", GOTO="anaconda_mdraid_member" ATTR{md/array_state}=="|clear|inactive", GOTO="anaconda_mdraid_member" LABEL="md_ignore_state" -IMPORT{program}="/usr/sbin/mdadm --detail --export $tempnode" +IMPORT{program}="$env{ANACBIN}/mdadm --detail --export $tempnode" ENV{DEVTYPE}=="disk", ENV{MD_NAME}=="?*", SYMLINK+="disk/by-id/md-name-$env{MD_NAME}", OPTIONS+="string_escape=replace" ENV{DEVTYPE}=="disk", ENV{MD_UUID}=="?*", SYMLINK+="disk/by-id/md-uuid-$env{MD_UUID}" ENV{DEVTYPE}=="disk", ENV{MD_DEVNAME}=="?*", SYMLINK+="md/$env{MD_DEVNAME}" @@ -42,13 +45,13 @@ ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk LABEL="anaconda_mdraid_member" # probe raid metadata of mdraid member devices -ENV{ID_FS_TYPE}=="linux_raid_member", IMPORT{program}="/usr/sbin/mdadm --examine --export $tempnode" +ENV{ID_FS_TYPE}=="linux_raid_member", IMPORT{program}="$env{ANACBIN}/mdadm --examine --export $tempnode" # probe metadata of LVM2 physical volumes -ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/usr/sbin/lvm pvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -opv_name,pv_uuid,pv_size,vg_name,vg_uuid,pv_pe_count,pv_pe_alloc_count,pe_start $tempnode" +ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="$env{ANACBIN}/lvm pvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -opv_name,pv_uuid,pv_size,vg_name,vg_uuid,pv_pe_count,pv_pe_alloc_count,pe_start $tempnode" ENV{LVM2_VG_NAME}!="?*", GOTO="anaconda_end" -ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/usr/sbin/lvm vgs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -ouuid,size,free,extent_size,extent_count,free_count,pv_count $env{LVM2_VG_NAME}" -ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="/usr/sbin/lvm lvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -olv_name,lv_uuid,lv_size $env{LVM2_VG_NAME}" +ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="$env{ANACBIN}/lvm vgs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -ouuid,size,free,extent_size,extent_count,free_count,pv_count $env{LVM2_VG_NAME}" +ENV{ID_FS_TYPE}=="LVM2_member", IMPORT{program}="$env{ANACBIN}/lvm lvs --units k --nosuffix --nameprefixes --rows --unquoted --noheadings -olv_name,lv_uuid,lv_size $env{LVM2_VG_NAME}" LABEL="anaconda_end" -- cgit From 60ccf355b3f59b4f3ebd7e5d552238149549ca71 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 13 Mar 2009 19:32:29 -0500 Subject: Fix display of LV format type for encrypted LVs. --- iw/partition_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index 7b22aabd1..c3b2dbc69 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -718,7 +718,7 @@ class PartitionWindow(InstallWindow): self.tree[iter]['Format'] = self.checkmark_pixbuf self.tree[iter]['IsFormattable'] = format.formattable self.tree[iter]['IsLeaf'] = True - self.tree[iter]['Type'] = lv.format.name + self.tree[iter]['Type'] = format.name #self.tree[iter]['Start'] = "" #self.tree[iter]['End'] = "" -- cgit From 0e4e9ab3f444987891c0131b0c5944403b125c46 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Sat, 14 Mar 2009 12:44:04 -0500 Subject: Add support for kickstart's '--initlabel' option to clearpart. - Move disklabel initialization into a method of DiskDevice. - Add a new keyword argument, initlabel, to the DiskDevice and DMRaidArrayDevice constructors. - Include Storage.protectedPartitions, Storage.reinitializeDisks, and Storage.clearPartDisks in DeviceTree's constructor argument list. - Pass the value of reinitializeDisks to the DiskDevice and DMRaidArrayDevice constructors unless the disk contains protected partitions or the disk is not in clearPartDisks. --- storage/__init__.py | 6 +++++ storage/devices.py | 41 +++++++++++++++++++++++----------- storage/devicetree.py | 62 ++++++++++++++++++++++++++++++++++++++++----------- 3 files changed, 83 insertions(+), 26 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index c786b63ca..820715683 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -172,6 +172,9 @@ class Storage(object): self.devicetree = DeviceTree(intf=self.anaconda.intf, ignored=self.ignoredDisks, exclusive=self.exclusiveDisks, + clear=self.clearPartDisks, + reinitializeDisks=self.reinitializeDisks, + protected=self.protectedPartitions, zeroMbr=self.zeroMbr, passphrase=self.encryptionPassphrase, luksDict=self.__luksDevs) @@ -215,6 +218,9 @@ class Storage(object): self.devicetree = DeviceTree(intf=self.anaconda.intf, ignored=self.ignoredDisks, exclusive=self.exclusiveDisks, + clear=self.clearPartDisks, + reinitializeDisks=self.reinitializeDisks, + protected=self.protectedPartitions, zeroMbr=self.zeroMbr, passphrase=self.encryptionPassphrase, luksDict=self.__luksDevs) diff --git a/storage/devices.py b/storage/devices.py index 39ffcf74d..aa70f2158 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -695,7 +695,7 @@ class DiskDevice(StorageDevice): def __init__(self, device, format=None, size=None, major=None, minor=None, sysfsPath='', \ - parents=None, initcb=None): + parents=None, initcb=None, initlabel=None): """ Create a DiskDevice instance. Arguments: @@ -713,6 +713,7 @@ class DiskDevice(StorageDevice): removable -- whether or not this is a removable device initcb -- the call back to be used when initiating disk. + initlabel -- whether to start with a fresh disklabel DiskDevices always exist. @@ -723,20 +724,24 @@ class DiskDevice(StorageDevice): self.partedDevice = None self.partedDisk = None + log.debug("looking up parted Device: %s" % self.path) self.partedDevice = parted.Device(path=self.path) if not self.partedDevice: raise DeviceError("cannot find parted device instance") + log.debug("creating parted Disk: %s" % self.path) - try: - self.partedDisk = parted.Disk(device=self.partedDevice) - except _ped.DiskLabelException: - # if we have a cb function use it. else an error. - if initcb is not None and initcb(): - self.partedDisk = parted.freshDisk(device=self.partedDevice, \ - ty = platform.getPlatform(None).diskType) - else: - raise DeviceUserDeniedFormatError("User prefered to not format.") + if initlabel: + self.partedDisk = self.freshPartedDisk() + else: + try: + self.partedDisk = parted.Disk(device=self.partedDevice) + except _ped.DiskLabelException: + # if we have a cb function use it. else an error. + if initcb is not None and initcb(): + self.partedDisk = self.freshPartedDisk() + else: + raise DeviceUserDeniedFormatError("User prefered to not format.") # We save the actual state of the disk here. Before the first # modification (addPartition or removePartition) to the partition @@ -754,6 +759,11 @@ class DiskDevice(StorageDevice): "partedDevice": self.partedDevice}) return s + def freshPartedDisk(self): + log_method_call(self, self.name) + labelType = platform.getPlatform(None).diskType + return parted.freshDisk(device=self.partedDevice, ty=labelType) + @property def size(self): """ The disk's size in MB """ @@ -2242,8 +2252,9 @@ class DMRaidArrayDevice(DiskDevice): _packages = ["dmraid"] devDir = "/dev/mapper" - def __init__(self, name, raidSet=None, level=None, format=None, size=None, - major=None, minor=None, parents=None, sysfsPath='', initcb=None): + def __init__(self, name, raidSet=None, level=None, format=None, + size=None, major=None, minor=None, parents=None, + sysfsPath='', initcb=None, initlabel=None): """ Create a DMRaidArrayDevice instance. Arguments: @@ -2258,6 +2269,9 @@ class DMRaidArrayDevice(DiskDevice): sysfsPath -- sysfs device path size -- the device's size format -- a DeviceFormat instance + + initcb -- the call back to be used when initiating disk. + initlabel -- whether to start with a fresh disklabel """ if isinstance(parents, list): for parent in parents: @@ -2265,7 +2279,8 @@ class DMRaidArrayDevice(DiskDevice): raise ValueError("parent devices must contain dmraidmember format") DiskDevice.__init__(self, name, format=format, size=size, major=major, minor=minor, parents=parents, - sysfsPath=sysfsPath, initcb=initcb) + sysfsPath=sysfsPath, initcb=initcb, + initlabel=initlabel) self.formatClass = get_device_format_class("dmraidmember") if not self.formatClass: diff --git a/storage/devicetree.py b/storage/devicetree.py index df5a6eddb..66d053efd 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -22,6 +22,7 @@ import os import block +import re from errors import * from devices import * @@ -155,15 +156,19 @@ class DeviceTree(object): except for resize actions. """ - def __init__(self, intf=None, ignored=[], exclusive=[], - zeroMbr=None, passphrase=None, luksDict=None): + def __init__(self, intf=None, ignored=[], exclusive=[], clear=[], + zeroMbr=None, reinitializeDisks=None, protected=[], + passphrase=None, luksDict=None): # internal data members self._devices = [] self._actions = [] self.intf = intf self.exclusiveDisks = exclusive + self.clearPartDisks = clear self.zeroMbr = zeroMbr + self.reinitializeDisks = reinitializeDisks + self.protectedPartitions = protected self.__passphrase = passphrase self.__luksDevs = {} if luksDict and isinstance(luksDict, dict): @@ -985,11 +990,25 @@ class DeviceTree(object): else: cb = lambda: questionInitializeDisk(self.intf, name) + # if the disk contains protected partitions we will + # not wipe the disklabel even if clearpart --initlabel + # was specified + if not self.clearPartDisks or name in self.clearPartDisks: + initlabel = self.reinitializeDisks + + for protected in self.protectedPartitions: + _p = "/sys/%s/%s" % (sysfs_path, protected) + if os.path.exists(os.path.normpath(_p)): + initlabel = False + break + else: + initlabel = False + device = DiskDevice(name, major=udev_device_get_major(info), minor=udev_device_get_minor(info), sysfsPath=sysfs_path, - initcb=cb) + initcb=cb, initlabel=initlabel) self._addDevice(device) except DeviceUserDeniedFormatError: #drive not initialized? self.addIgnoredDisk(name) @@ -1162,18 +1181,35 @@ class DeviceTree(object): rs.activate(mknod=True) # Create the DMRaidArray + if self.zeroMbr: + cb = lambda: True + else: + cb = lambda: questionInitializeDisk(self.intf, + rs.name) + + if not self.clearPartDisks or \ + rs.name in self.clearPartDisks: + # if the disk contains protected partitions + # we will not wipe the disklabel even if + # clearpart --initlabel was specified + initlabel = self.reinitializeDisks + for protected in self.protectedPartitions: + disk_name = re.sub(r'p\d+$', protected) + if disk_name != protected and \ + disk_name == rs.name: + initlabel = False + break + else: + initlabel = False + try: - if self.zeroMbr: - cb = lambda: True - else: - cb = lambda: questionInitializeDisk(self.intf, - rs.name) dm_array = DMRaidArrayDevice(rs.name, - major=major, minor=minor, - raidSet=rs, - level=rs.level, - parents=[device], - initcb=cb) + major=major, minor=minor, + raidSet=rs, + level=rs.level, + parents=[device], + initcb=cb, + initlabel=initlabel) self._addDevice(dm_array) # Use the rs's object on the device. -- cgit From b903963407b694f0e01692b9edd130ee163db260 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 10 Mar 2009 09:37:50 -1000 Subject: Let users set the size property of StorageDevices. For resize operations, we need the ability to set a new size for the StorageDevice before performing the resize. Raise DeviceError exception if newsize > maxSize --- storage/devices.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index aa70f2158..1ec46489f 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -587,11 +587,13 @@ class StorageDevice(Device): def _setSize(self, newsize): """ Set the device's size to a new value. """ - # FIXME: this should involve some verification + if newsize > self.maxSize: + raise DeviceError("device cannot be larger than %s MB" % + (self.maxSize(),)) self._size = newsize size = property(lambda x: x._getSize(), - #lambda x, y: x._setSize(y), + lambda x, y: x._setSize(y), doc="The device's size, accounting for pending changes") @property -- cgit From 8035900a327e000c7f6c203283c3954170368195 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Fri, 13 Mar 2009 10:09:49 -1000 Subject: Handle resizing when setting targetSize for PartitionDevice If the user changes the targetSize of a PartitionDevice, calculate a new geometry for the partition and mark the PartitionDevice for resizing. The new values are now in-memory and written to disk when resize() is called by the action's execute() method. --- storage/devices.py | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/storage/devices.py b/storage/devices.py index 1ec46489f..8b6f7efc6 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -482,7 +482,17 @@ class StorageDevice(Device): self.format = format self.fstabComment = "" - self.targetSize = self._size + self._targetSize = self._size + + def _getTargetSize(self): + return self._targetSize + + def _setTargetSize(self, newsize): + self._targetSize = newsize + + targetSize = property(lambda s: s._getTargetSize(), + lambda s, v: s._setTargetSize(v), + doc="Target size of this device") def __str__(self): s = Device.__str__(self) @@ -899,6 +909,8 @@ class PartitionDevice(StorageDevice): self.bootable = False + self._resize = False + StorageDevice.__init__(self, name, format=format, size=size, major=major, minor=minor, exists=exists, sysfsPath=sysfsPath, parents=parents) @@ -957,6 +969,30 @@ class PartitionDevice(StorageDevice): "partedPart": self.partedPartition, "disk": self.disk}) return s + def _setTargetSize(self, newsize): + # a change in the target size means we need to resize the disk + # if targetSize is 0, it means we are initializing, so don't jump + # the gun and assume we're resizing + if newsize != self.targetSize and self.targetSize != 0: + self._resize = True + + self._targetSize = newsize + + if self._resize: + currentGeom = self.partedPartition.geometry + currentDev = currentGeom.device + newLen = long(self.targetSize * 1024 * 1024) / currentDev.sectorSize + geom = parted.Geometry(device=currentDev, + start=currentGeom.start, + length=newLen) + constraint = parted.Constraint(exactGeom=geom) + + partedDisk = self.disk.partedDisk + partedDisk.setPartitionGeometry(partition=self.partedPartition, + constraint=constraint, + start=geom.start, end=geom.end) + self.partedPartition = None + @property def partType(self): """ Get the partition's type (as parted constant). """ @@ -1112,6 +1148,16 @@ class PartitionDevice(StorageDevice): self.exists = True self.setup() + def resize(self, intf=None): + """ Resize the device. + + self.targetSize must be set to the new size. + """ + log_method_call(self, self.name, status=self.status) + + if self._resize: + self.disk.commit() + def destroy(self): """ Destroy the device. """ log_method_call(self, self.name, status=self.status) -- cgit From 2bf4302cdd362d482a61a49063e64dcf2e880e20 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Wed, 11 Mar 2009 13:29:10 -1000 Subject: Raise FSResizeError if filesystem cannot be resized. Raise FSResizeError exception if we enter this method, but the format is not resizable. The calling methods have checks to prevent this from happening, so if we get this far, it should be an exception. --- storage/formats/fs.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index f46785221..7dd136f67 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -323,8 +323,7 @@ class FS(DeviceFormat): raise FSResizeError("filesystem does not exist", self.device) if not self.resizable: - # should this instead raise an exception? - return + raise FSResizeError("filesystem not resizable", self.device) if self.targetSize == self.currentSize: return -- cgit From 291044a368ffbd51c8ff06547b30a601edaf509d Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Wed, 11 Mar 2009 23:07:52 -1000 Subject: Improve resizeDialog text. Change the title and indicate the entry field is expecting megabytes. --- ui/autopart.glade | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/autopart.glade b/ui/autopart.glade index ae6909672..333527423 100644 --- a/ui/autopart.glade +++ b/ui/autopart.glade @@ -341,7 +341,7 @@ 12 True - Which Partition to resize + Volume to Resize GTK_WINDOW_TOPLEVEL GTK_WIN_POS_CENTER False @@ -517,7 +517,7 @@ True - <b>Resize _target:</b> + <b>Resize _target (in MB):</b> True True GTK_JUSTIFY_LEFT -- cgit From 4d76e7efeffa76c05e6f70afdd1b2ad386f97bf0 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 12 Mar 2009 14:10:56 -1000 Subject: Report when we cannot find any free space partitions. If the user is performing an automatic partitioning install and we fail to find any usable free space partitions, tell the user what happened and that they should choose another partitioning method. --- storage/partitioning.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/storage/partitioning.py b/storage/partitioning.py index cf151d1da..f4c40f2f8 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -167,6 +167,15 @@ def doAutoPartition(anaconda): if anaconda.id.storage.doAutoPart: (disks, devs) = _createFreeSpacePartitions(anaconda) + + if disks == []: + anaconda.intf.messageWindow(_("Error Partitioning"), + _("Could not find enough free space " + "for automatic partitioning, please " + "use another partitioning method."), + custom_icon='error') + return DISPATCH_BACK + _schedulePartitions(anaconda, disks) # sanity check the individual devices -- cgit From b7d49b9710186cf437a9cb61c0d4eed1a70024f7 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Fri, 13 Mar 2009 17:58:40 -1000 Subject: Add resizeArgs for Ext2FS and fix it for BtrFS. Add a resizeArgs property for Ext2FS, make sure the size gets the 'M' suffix and the arguments are in the right order. Make sure the size gets the 'm' suffix in the BtrFS.resizeArgs property. --- storage/formats/fs.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 7dd136f67..280f8b383 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -721,6 +721,11 @@ class Ext2FS(FS): def isDirty(self): return isys.ext2IsDirty(self.device) + @property + def resizeArgs(self): + argv = ["-p", self.device, "%dM" % (self.targetSize,)] + return argv + register_device_format(Ext2FS) @@ -803,7 +808,7 @@ class BTRFS(FS): @property def resizeArgs(self): - argv = ["-r", "%d" % (self.targetSize,), self.device] + argv = ["-r", "%dm" % (self.targetSize,), self.device] return argv @property -- cgit From bd4c05cc93323122f3189f7f982d8fa0f7c90b13 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Fri, 13 Mar 2009 18:03:22 -1000 Subject: Use os.statvfs() to get existing filesystem size. Mount the filesystem in a temporary location, call os.statvfs(), compute filesystem size in megabytes. --- storage/formats/fs.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 280f8b383..bc183c2de 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -18,6 +18,7 @@ # Red Hat, Inc. # # Red Hat Author(s): Dave Lehman +# David Cantrell # """ Filesystem classes for use by anaconda. @@ -27,6 +28,7 @@ - bug 472127: allow creation of tmpfs filesystems (/tmp, /var/tmp, &c) """ import os +import tempfile import isys from ..errors import * @@ -150,8 +152,12 @@ class FS(DeviceFormat): self.mountpoint = kwargs.get("mountpoint") self.mountopts = kwargs.get("mountopts") self.label = kwargs.get("label") + # filesystem size does not necessarily equal device size self._size = kwargs.get("size") + if self.exists: + self._size = self._getExistingSize() + self._targetSize = self._size self._mountpoint = None # the current mountpoint when mounted @@ -187,6 +193,27 @@ class FS(DeviceFormat): size = property(_getSize, doc="This filesystem's size, accounting " "for pending changes") + def _getExistingSize(self): + """ Determine the size of this filesystem. Filesystem must + exist. + """ + size = 0 + + if self.mountable: + origMountPoint = self._mountpoint + + (f, tmppath) = tempfile.mkdtemp(prefix='getsize', dir='/tmp') + self.mount(mountpoint=tmppath) + buf = os.statvfs(tmppath) + self.unmount() + os.rmdir(tmppath) + + self._mountpoint = origMountPoint + + size = (buf.f_frsize * buf.f_blocks) / 1024.0 / 1024.0 + + return size + @property def currentSize(self): """ The filesystem's current actual size. """ -- cgit From 7d23bfb9a4f9c46686c096eab79464262cd3eec7 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Wed, 11 Mar 2009 16:07:07 -1000 Subject: Create separate resize actions for formats and devices. In the 'Shrink current system' dialog box, create an action to resize the format and separate one to resize the device. These are registered in the Storage object when the function returns. Catch exceptions from the actions and display error messages for the user if necessary. --- iw/autopart_type.py | 80 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/iw/autopart_type.py b/iw/autopart_type.py index b634cc964..6bbca11b9 100644 --- a/iw/autopart_type.py +++ b/iw/autopart_type.py @@ -45,18 +45,14 @@ def whichToResize(storage, intf): def comboCB(combo, resizeSB): # partition to resize changed, let's update our spinbutton part = getActive(combo) - if part.targetSize is not None: - value = part.targetSize - else: - value = part.size reqlower = part.minSize requpper = part.maxSize adj = resizeSB.get_adjustment() adj.lower = reqlower adj.upper = requpper - adj.value = value - adj.set_value(value) + adj.value = reqlower + adj.set_value(reqlower) (dxml, dialog) = gui.getGladeWidget("autopart.glade", "resizeDialog") @@ -69,7 +65,6 @@ def whichToResize(storage, intf): combo.set_attributes(crt, text = 0) combo.connect("changed", comboCB, dxml.get_widget("resizeSB")) - found = False biggest = -1 for part in storage.partitions: if not part.exists: @@ -77,20 +72,27 @@ def whichToResize(storage, intf): # Resize the following storage types: # resizable filesystem (e.g., ext3 or ntfs) on resizable partition + # resizable filesystem on a resizable logical volume + entry = None if part.resizable and part.format.resizable: + entry = ("%s (%s, %d MB)" % (part.name, + part.format.name, + math.floor(part.format.size)), + part) + + if entry: i = store.append(None) - store[i] = ("%s (%s, %d MB)" %(part.name, - part.format.name, - math.floor(part.format.currentSize)), - part) - if part.targetSize is not None: - combo.set_active_iter(i) - found = True + store[i] = entry + combo.set_active_iter(i) + + if biggest == -1: + biggest = i else: - if biggest < 0 or req.size > store.get_value(biggest, 1).size: + current = store.get_value(biggest, 1) + if part.format.targetSize > current.format.targetSize: biggest = i - if not found and biggest > 0: + if biggest > -1: combo.set_active_iter(biggest) if len(store) == 0: @@ -100,20 +102,43 @@ def whichToResize(storage, intf): "physical partitions with specific filesystems " "can be resized."), type="warning", custom_icon="error") - return gtk.RESPONSE_CANCEL + return (gtk.RESPONSE_CANCEL, []) gui.addFrame(dialog) dialog.show_all() - rc = dialog.run() - if rc != gtk.RESPONSE_OK: - dialog.destroy() - return rc + runResize = True + + while runResize: + rc = dialog.run() + if rc != gtk.RESPONSE_OK: + dialog.destroy() + return (rc, []) + + request = getActive(combo) + newSize = dxml.get_widget("resizeSB").get_value_as_int() + actions = [] + + try: + actions.append(ActionResizeFormat(request, newSize)) + except ValueError as e: + intf.messageWindow(_("Resize FileSystem Error"), + _("%s: %s") % (request.format.device, + e.message,), + type="warning", custom_icon="error") + continue + + try: + actions.append(ActionResizeDevice(request, newSize)) + except ValueError as e: + intf.messageWindow(_("Resize Device Error"), + _("%s: %s") % (request.name, e.message,), + type="warning", custom_icon="error") + continue + + runResize = False - request = getActive(combo) - newSize = dxml.get_widget("resizeSB").get_value_as_int() - action = ActionResizeFormat(request, newSize) dialog.destroy() - return (rc, action) + return (rc, actions) class PartitionTypeWindow(InstallWindow): def __init__(self, ics): @@ -134,9 +159,10 @@ class PartitionTypeWindow(InstallWindow): self.dispatch.skipStep("bootloader", skip = 0) else: if val == -2: - (rc, action) = whichToResize(self.storage, self.intf) + (rc, actions) = whichToResize(self.storage, self.intf) if rc == gtk.RESPONSE_OK: - self.storage.devicetree.registerAction(action) + for action in actions: + self.storage.devicetree.registerAction(action) else: raise gui.StayOnScreen -- cgit From bbc327f945c06a211c40205aae56ea8447c471cc Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Mon, 16 Mar 2009 14:08:51 +0100 Subject: devicetree.py has _ignoredDisks instead of ignoredDisks. --- storage/devicetree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index 66d053efd..a2d038679 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1166,7 +1166,7 @@ class DeviceTree(object): self.addIgnoredDisk(device.name) return - elif rs.name in self.ignoredDisks: + elif rs.name in self._ignoredDisks: # If the rs is being ignored, we should ignore device too. self.addIgnoredDisk(device.name) return -- cgit From 158689fb6050aca9d8921248fe9cb57d4b168a7f Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Mon, 16 Mar 2009 12:56:49 +0100 Subject: Initialize attribute _mountpoint before using it --- storage/formats/fs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index bc183c2de..2e1e778f9 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -155,11 +155,11 @@ class FS(DeviceFormat): # filesystem size does not necessarily equal device size self._size = kwargs.get("size") + self._mountpoint = None # the current mountpoint when mounted if self.exists: self._size = self._getExistingSize() self._targetSize = self._size - self._mountpoint = None # the current mountpoint when mounted def _setTargetSize(self, newsize): """ Set a target size for this filesystem. """ -- cgit From f8ce41043ea074ccc1f991712af8feab5f479763 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Mon, 16 Mar 2009 13:00:23 +0100 Subject: Fix tempfile.mkdtemp call. --- storage/formats/fs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 2e1e778f9..545970c17 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -202,7 +202,7 @@ class FS(DeviceFormat): if self.mountable: origMountPoint = self._mountpoint - (f, tmppath) = tempfile.mkdtemp(prefix='getsize', dir='/tmp') + tmppath = tempfile.mkdtemp(prefix='getsize', dir='/tmp') self.mount(mountpoint=tmppath) buf = os.statvfs(tmppath) self.unmount() -- cgit From 684322c3df2959ec268b63220546bcef83534656 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Mon, 16 Mar 2009 13:08:54 +0100 Subject: isys.umount removes mount directory by default --- storage/formats/fs.py | 1 - 1 file changed, 1 deletion(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 545970c17..fa931369b 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -206,7 +206,6 @@ class FS(DeviceFormat): self.mount(mountpoint=tmppath) buf = os.statvfs(tmppath) self.unmount() - os.rmdir(tmppath) self._mountpoint = origMountPoint -- cgit From 59253764fce8649078e7f23d826eca080a14b2b1 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 16 Mar 2009 10:29:23 -0400 Subject: Fix the obvious and stupid typo (#490296). --- yuminstall.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/yuminstall.py b/yuminstall.py index dc541b571..eff50ba61 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -1493,7 +1493,7 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon if not upgrade: anaconda.id.storage.fsset.write(anaconda.rootPath) # rootpath mode doesn't have this file around - if os.access("/etc/modprobe.d/anaconda".conf, os.R_OK): + if os.access("/etc/modprobe.d/anaconda.conf", os.R_OK): shutil.copyfile("/etc/modprobe.d/anaconda.conf", anaconda.rootPath + "/etc/modprobe.d/anaconda.conf") anaconda.id.network.write(instPath=anaconda.rootPath, anaconda=anaconda) -- cgit From 2b9670b91aee941037de5e4ef316f4021c8f968a Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 16 Mar 2009 10:40:23 -0400 Subject: clampPVSize -> clampSize in lvm.py (#490295). --- iw/lvm_dialog_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index 6aa5ef3b0..3f066d7a7 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -74,7 +74,7 @@ class VolumeGroupEditor: pesize = self.vg.peSize # FIXME: move this logic into a property of LVMVolumeGroupDevice - pvsize = lvm.clampPVSize(pv.size, pesize) - int(pesize/1024) + pvsize = lvm.clampSize(pv.size, pesize) - int(pesize/1024) if first: minpvsize = pvsize first = 0 -- cgit From 0dc62d034ecc917345fa2c206bddf5f1bde6b3a6 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 16 Mar 2009 10:44:28 -0400 Subject: editPartitionRequest -> editPartition in iw/partition_gui.py (#490384). --- iw/partition_gui.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index c3b2dbc69..c7097b0c2 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -1270,9 +1270,9 @@ class PartitionWindow(InstallWindow): if createRAIDpart.get_active(): member = self.storage.newPartition(fmt_type="software RAID", size=200) - rc = self.editPartitionRequest(member, - isNew = 1, - restrictfs=["software RAID"]) + rc = self.editPartition(member, + isNew = 1, + restrictfs=["software RAID"]) elif createRAIDdev.get_active(): array = self.storage.newMDArray(fmt_type=self.storage.defaultFSType) self.editRaidArray(array, isNew=1) -- cgit From 9514763cb6f1faf74bfa4d434ce5125d8a55475c Mon Sep 17 00:00:00 2001 From: Martin Gracik Date: Fri, 13 Mar 2009 14:56:07 +0100 Subject: Fixed the names of the variables for lvm.py functions. lv_name is passed to the function, not lv_path, which was used in the raise statements. --- storage/devicelibs/lvm.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py index 0a1971152..8b755a4e0 100644 --- a/storage/devicelibs/lvm.py +++ b/storage/devicelibs/lvm.py @@ -367,7 +367,7 @@ def lvremove(vg_name, lv_name): searchPath=1) if rc: - raise LVMError("lvremove failed for %s" % lv_path) + raise LVMError("lvremove failed for %s" % lv_name) def lvresize(vg_name, lv_name, size): args = ["lvresize"] + \ @@ -381,7 +381,7 @@ def lvresize(vg_name, lv_name, size): searchPath=1) if rc: - raise LVMError("lvresize failed for %s" % lv_path) + raise LVMError("lvresize failed for %s" % lv_name) def lvactivate(vg_name, lv_name): # see if lvchange accepts paths of the form 'mapper/$vg-$lv' @@ -394,7 +394,7 @@ def lvactivate(vg_name, lv_name): stderr = "/dev/tty5", searchPath=1) if rc: - raise LVMError("lvactivate failed for %s" % lv_path) + raise LVMError("lvactivate failed for %s" % lv_name) def lvdeactivate(vg_name, lv_name): args = ["lvchange", "-a", "n"] + \ @@ -407,5 +407,5 @@ def lvdeactivate(vg_name, lv_name): searchPath=1) if rc: - raise LVMError("lvdeactivate failed for %s" % lv_path) + raise LVMError("lvdeactivate failed for %s" % lv_name) -- cgit From 00e6a171b73c2312bd138cbe10bf5867fe1a9d13 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 16 Mar 2009 11:55:11 -0400 Subject: Handle FTP servers that both want and don't want PASS after USER (#490350). Before, we were sending PASS only if we got a 230 (success) response to USER and not providing PASS if we got a 331 (gimme a password) response. That's entirely the wrong behavior. --- loader/ftp.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/loader/ftp.c b/loader/ftp.c index 8f8a585f9..15b4f8326 100644 --- a/loader/ftp.c +++ b/loader/ftp.c @@ -330,7 +330,7 @@ int ftpOpen(char *host, int family, char *name, char *password, /* FTP does not require that USER be followed by PASS. Anonymous logins * in particular do not need any password. */ - if (!strncmp(userReply, "230", 3)) { + if (strncmp(userReply, "230", 3) != 0) { if ((rc = ftpCommand(sock, NULL, "PASS", password, NULL))) { close(sock); return rc; -- cgit From 565d74053dfb57d171898d842d24bc01ebb43a9c Mon Sep 17 00:00:00 2001 From: David Lehman Date: Sun, 15 Mar 2009 15:40:48 -0500 Subject: Fix lots of buggy behavior in the partition dialog. Re-editing new partitions was generating a new request instead of modifying the original request. We were also trying to pass in a format to the ActionDestroyFormat constructor, which is wrong -- they all take a device argument. Lastly, the conditional for showing the "new" filesystem options was wrong, which was causing both it and the "preexist" fs options to get added to the dialog on new request re-edits. --- iw/partition_dialog_gui.py | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index 53d26f0b4..c3796717c 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -159,15 +159,18 @@ class PartitionEditor: if disk.name == drive: disks.append(disk) - # TODO: when will we set bootable? - request = self.storage.newPartition(size=size, - grow=grow, - maxsize=maxsize, - primary=primary, - parents=disks) + if self.isNew: + request = self.storage.newPartition(size=size, + grow=grow, + maxsize=maxsize, + primary=primary, + parents=disks) + else: + request = self.origrequest format = fmt_class(mountpoint=mountpoint) - if self.lukscb and self.lukscb.get_active(): + if self.lukscb and self.lukscb.get_active() and \ + request.format.type != "luks": luksformat = format format = getFormat("luks", passphrase=self.storage.encryptionPassphrase) @@ -179,13 +182,21 @@ class PartitionEditor: # destroy the luks format and the mapped device luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] if luksdev: - actions.append(ActionDestroyFormat(luksdev.format)) + actions.append(ActionDestroyFormat(luksdev)) actions.append(ActionDestroyDevice(luksdev)) luksdev = None - actions.append(ActionDestroyFormat(self.origrequest)) + actions.append(ActionDestroyFormat(request)) + + if self.isNew: + # we're all set, so create the actions + actions.append(ActionCreateDevice(request)) + else: + request.req_size = size + request.req_grow = grow + request.req_max_size = maxsize + request.req_primary = primary + request.req_disks = disks - # we're all set, so create the actions - actions.append(ActionCreateDevice(request)) actions.append(ActionCreateFormat(request, format)) if luksdev: actions.append(ActionCreateDevice(luksdev)) @@ -286,7 +297,7 @@ class PartitionEditor: row = row + 1 # Partition Type - if not self.origrequest.format.exists: + if not self.origrequest.exists: lbl = createAlignedLabel(_("File System _Type:")) maintable.attach(lbl, 0, 1, row, row + 1) -- cgit From b7dae7d2a553aa76723825627995b6065e3ec504 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Sun, 15 Mar 2009 15:49:20 -0500 Subject: Fix creation of fs options for preexisting encrypted devices. Previously we just passed in the device on which we would be creating the filesystem, but that doesn't make it easy to know if we initially plan to format the device or if the device is encrypted. --- iw/lvm_dialog_gui.py | 6 ++++-- iw/partition_dialog_gui.py | 2 +- iw/partition_ui_helpers_gui.py | 32 ++++++++++++++++++-------------- iw/raid_dialog_gui.py | 6 ++++-- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index 3f066d7a7..be3e246b3 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -393,9 +393,11 @@ class VolumeGroupEditor: row = 0 if lv.format.type == "luks": - usedev = self.findLUKSDev(lv) + luksdev = self.findLUKSDev(lv) + usedev = luksdev format = usedev.format elif lv: + luksdev = None usedev = lv format = lv.format @@ -471,7 +473,7 @@ class VolumeGroupEditor: self.fsoptionsDict = {} if lv.exists: - (row, self.fsoptionsDict) = createPreExistFSOptionSection(usedev, maintable, row, mountCombo, self.storage, ignorefs = ["software RAID", "physical volume (LVM)", "vfat"]) + (row, self.fsoptionsDict) = createPreExistFSOptionSection(lv, maintable, row, mountCombo, self.storage, ignorefs = ["software RAID", "physical volume (LVM)", "vfat"], luksdev=luksdev) # checkbutton for encryption using dm-crypt/LUKS if not lv.exists: diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index c3796717c..802161648 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -366,7 +366,7 @@ class PartitionEditor: self.fsoptionsDict = {} if self.origrequest.exists and \ not self.storage.isProtected(self.origrequest): - (row, self.fsoptionsDict) = createPreExistFSOptionSection(usereq, maintable, row, self.mountCombo, self.storage) + (row, self.fsoptionsDict) = createPreExistFSOptionSection(self.origrequest, maintable, row, self.mountCombo, self.storage, luksdev=luksdev) # size options if not self.origrequest.exists: diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index b8cae4a6b..5cd75c20a 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -308,36 +308,40 @@ def noformatCB(widget, data): resizesb - spinbutton with resize target """ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, - partitions, ignorefs=[]): + partitions, ignorefs=[], luksdev=None): rc = {} - ofstype = origrequest.format + if luksdev: + origfs = luksdev.format + else: + origfs = origrequest.format + formatcb = gtk.CheckButton(label=_("_Format as:")) maintable.attach(formatcb, 0, 1, row, row + 1) - formatcb.set_active(istruefalse(not origrequest.format.exists)) + formatcb.set_active(istruefalse(not origfs.exists)) rc["formatcb"] = formatcb - fstypeCombo = createFSTypeMenu(ofstype, fstypechangeCB, + fstypeCombo = createFSTypeMenu(origfs, fstypechangeCB, mountCombo, ignorefs=ignorefs) fstypeCombo.set_sensitive(formatcb.get_active()) maintable.attach(fstypeCombo, 1, 2, row, row + 1) row += 1 rc["fstypeCombo"] = fstypeCombo - if not formatcb.get_active() and not origrequest.format.migrate: - mountCombo.set_data("prevmountable", origrequest.format.mountable) + if not formatcb.get_active() and not origfs.migrate: + mountCombo.set_data("prevmountable", origfs.mountable) # this gets added to the table a bit later on lukscb = gtk.CheckButton(_("_Encrypt")) - if origrequest.format.migratable: + if origfs.migratable: migratecb = gtk.CheckButton(label=_("Mi_grate filesystem to:")) - migratecb.set_active(istruefalse(origrequest.format.migrate)) + migratecb.set_active(istruefalse(origfs.migrate)) - migtypes = [origrequest.format.migrationTarget] + migtypes = [origfs.migrationTarget] maintable.attach(migratecb, 0, 1, row, row + 1) - migfstypeCombo = createFSTypeMenu(ofstype, + migfstypeCombo = createFSTypeMenu(origfs, None, None, availablefstypes = migtypes) migfstypeCombo.set_sensitive(migratecb.get_active()) @@ -346,14 +350,14 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, rc["migratecb"] = migratecb rc["migfstypeCombo"] = migfstypeCombo migratecb.connect("toggled", formatMigrateOptionCB, - (migfstypeCombo, mountCombo, ofstype, None, + (migfstypeCombo, mountCombo, origfs, None, fstypeCombo, formatcb)) else: migratecb = None migfstypeCombo = None formatcb.connect("toggled", formatMigrateOptionCB, - (fstypeCombo, mountCombo, ofstype, lukscb, + (fstypeCombo, mountCombo, origfs, lukscb, migfstypeCombo, migratecb)) if origrequest.resizable: @@ -369,7 +373,7 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, reqlower = origrequest.minSize requpper = origrequest.maxSize - if origrequest.format.exists: + if origfs.exists: lower = reqlower else: lower = 1 @@ -387,7 +391,7 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, formatcb.connect("toggled", formatOptionResizeCB, resizesb) - if origrequest.format.type == "luks": + if luksdev: lukscb.set_active(1) lukscb.set_data("encrypted", 1) else: diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index 099a9c965..3ae2f6e09 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -332,9 +332,11 @@ class RaidEditor: self.lukscb.set_data("formatstate", 1) if origrequest.format.type == "luks": - usedev = self.storage.devicetree.getChildren(origrequest)[0] + luksdev = self.storage.devicetree.getChildren(origrequest)[0] + usedev = luksdev format = usedev.format else: + luksdev = None usedev = origrequest format = origrequest.format @@ -474,7 +476,7 @@ class RaidEditor: maintable.attach(self.lukscb, 0, 2, row, row + 1) row = row + 1 else: - (row, self.fsoptionsDict) = createPreExistFSOptionSection(usedev, maintable, row, self.mountCombo, self.storage) + (row, self.fsoptionsDict) = createPreExistFSOptionSection(origrequest, maintable, row, self.mountCombo, self.storage, luksdev=luksdev) # put main table into dialog dialog.vbox.pack_start(maintable) -- cgit From d7f6a0095ecb170e74ae67b7d6f7aca120050123 Mon Sep 17 00:00:00 2001 From: Will Woods Date: Mon, 16 Mar 2009 12:29:25 -0400 Subject: Use correct parse method for the upgrade command (#471232) --- kickstart.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kickstart.py b/kickstart.py index f24d6178a..06f6199de 100644 --- a/kickstart.py +++ b/kickstart.py @@ -855,7 +855,7 @@ class Timezone(commands.timezone.FC6_Timezone): class Upgrade(commands.upgrade.F11_Upgrade): def parse(self, args): - retval = commands.upgrade.FC3_Upgrade.parse(self, args) + retval = commands.upgrade.F11_Upgrade.parse(self, args) self.handler.id.setUpgrade(self.upgrade) return retval -- cgit From b58afa19d1b7df9e7b4e5caedbdb2480305c2070 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 13 Mar 2009 17:35:27 -0400 Subject: Ignore disk devices with missing media (#488800). There's a class of device that shows up as a disk drive, but is removable and doesn't necessarily have media present all the time. This class contains things like USB CF readers, those 5-in-1 media readers on the fronts of some computers, and so forth. To fix this, I've added a mediaPresent method to DiskDevice that checks if self.partedDevice was created or not. We then need to check this throughout DiskDevice methods - basically, do nothing if we couldn't probe the device to begin with, but still recognize its existence. In order to keep the rest of the code clean, we need to weed out all devices without media present in Storage.disks. --- storage/__init__.py | 6 +++--- storage/devices.py | 61 +++++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 48 insertions(+), 19 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 820715683..416b4eea2 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -239,7 +239,7 @@ class Storage(object): def disks(self): """ A list of the disks in the device tree. - Ignored disks are not included. + Ignored disks are not included, as are disks with no media present. This is based on the current state of the device tree and does not necessarily reflect the actual on-disk state of the @@ -248,7 +248,7 @@ class Storage(object): disks = [] devices = self.devicetree.devices for d in devices: - if isinstance(devices[d], DiskDevice): + if isinstance(devices[d], DiskDevice) and devices[d].mediaPresent: disks.append(devices[d]) disks.sort(key=lambda d: d.name) return disks @@ -613,7 +613,7 @@ class Storage(object): def extendedPartitionsSupported(self): """ Return whether any disks support extended partitions.""" - for disk in self.disks: + for disk in filter(lambda disk: disk.mediaPresent, self.disks): if disk.partedDisk.supportsFeature(parted.DISK_TYPE_EXTENDED): return True return False diff --git a/storage/devices.py b/storage/devices.py index 8b6f7efc6..397a648b0 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -738,28 +738,38 @@ class DiskDevice(StorageDevice): self.partedDisk = None log.debug("looking up parted Device: %s" % self.path) - self.partedDevice = parted.Device(path=self.path) - if not self.partedDevice: - raise DeviceError("cannot find parted device instance") + + # We aren't guaranteed to be able to get a device. In particular, + # built-in USB flash readers show up as devices but do not always + # have any media present, so parted won't be able to find a device. + try: + self.partedDevice = parted.Device(path=self.path) + except _ped.DeviceException: + pass log.debug("creating parted Disk: %s" % self.path) - if initlabel: - self.partedDisk = self.freshPartedDisk() - else: - try: - self.partedDisk = parted.Disk(device=self.partedDevice) - except _ped.DiskLabelException: - # if we have a cb function use it. else an error. - if initcb is not None and initcb(): - self.partedDisk = self.freshPartedDisk() - else: - raise DeviceUserDeniedFormatError("User prefered to not format.") + if self.partedDevice: + if initlabel: + self.partedDisk = self.fresPartedDisk() + else: + try: + self.partedDisk = parted.Disk(device=self.partedDevice) + except _ped.DiskLabelException: + # if we have a cb function use it. else an error. + if initcb is not None and initcb(): + self.partedDisk = parted.freshDisk(device=self.partedDevice, \ + ty = platform.getPlatform(None).diskType) + else: + raise DeviceUserDeniedFormatError("User prefered to not format.") # We save the actual state of the disk here. Before the first # modification (addPartition or removePartition) to the partition # table we reset self.partedPartition to this state so we can # perform the modifications one at a time. - self._origPartedDisk = self.partedDisk.duplicate() + if self.partedDisk: + self._origPartedDisk = self.partedDisk.duplicate() + else: + self._origPartedDisk = None self.probe() @@ -776,9 +786,16 @@ class DiskDevice(StorageDevice): labelType = platform.getPlatform(None).diskType return parted.freshDisk(device=self.partedDevice, ty=labelType) + @property + def mediaPresent(self): + return self.partedDevice is not None + @property def size(self): """ The disk's size in MB """ + if not self.mediaPresent: + return 0 + if not self._size: self._size = self.partedDisk.device.getSize() return self._size @@ -790,12 +807,18 @@ class DiskDevice(StorageDevice): def removePartition(self, device): log_method_call(self, self.name, part=device.name) + if not self.mediaPresent: + raise DeviceError("cannot remove partition from disk %s which has no media" % self.name) + partition = self.partedDisk.getPartitionByPath(device.path) if partition: self.partedDisk.removePartition(partition) def addPartition(self, device): log_method_call(self, self.name, part=device.name) + if not self.mediaPresent: + raise DeviceError("cannot add partition to disk %s which has no media" % self.name) + for part in self.partedDisk.partitions: log.debug("disk %s: partition %s has geom %s" % (self.name, part.getDeviceNodeName(), @@ -815,7 +838,7 @@ class DiskDevice(StorageDevice): pyparted should be able to tell us anything we want to know. size, disklabel type, maybe even partition layout """ - if not 'parted' in globals().keys(): + if not self.mediaPresent or not 'parted' in globals().keys(): return log_method_call(self, self.name, size=self.size, partedDevice=self.partedDevice) @@ -829,6 +852,9 @@ class DiskDevice(StorageDevice): def commit(self, intf=None): """ Commit changes to the device. """ log_method_call(self, self.name, status=self.status) + if not self.mediaPresent: + raise DeviceError("cannot commit to disk %s which has no media" % self.name) + self.setupParents() self.setup() self.partedDisk.commit() @@ -836,6 +862,9 @@ class DiskDevice(StorageDevice): def destroy(self): """ Destroy the device. """ log_method_call(self, self.name, status=self.status) + if not self.mediaPresent: + raise DeviceError("cannot destroy disk %s which has no media" % self.name) + if self.status: self.format.destroy() -- cgit From 018d860776f1933ae9114a276c05721cc910b975 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 16 Mar 2009 14:11:31 +0100 Subject: Install udev rules in /lib/udev/rules.d instead of in runtime dir Install the udev rules in /lib/udev/rules.d instead of in runtime dir, so that they are available for use directly on the livecd. --- Makefile | 3 ++- anaconda.spec | 1 + scripts/upd-instroot | 1 - 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 388af21ff..dd45b29af 100644 --- a/Makefile +++ b/Makefile @@ -87,11 +87,12 @@ install: mkdir -p $(DESTDIR)/usr/bin mkdir -p $(DESTDIR)/usr/sbin mkdir -p $(DESTDIR)/etc/rc.d/init.d + mkdir -p $(DESTDIR)/lib/udev/rules.d mkdir -p $(DESTDIR)/$(PYTHONLIBDIR) mkdir -p $(DESTDIR)/$(RUNTIMEDIR) mkdir -p $(DESTDIR)/$(ANACONDADATADIR) - install -m 644 70-anaconda.rules $(DESTDIR)/$(RUNTIMEDIR) + install -m 644 70-anaconda.rules $(DESTDIR)/lib/udev/rules.d install -m 755 anaconda $(DESTDIR)/usr/sbin/anaconda install -m 755 mini-wm $(DESTDIR)/usr/bin/mini-wm diff --git a/anaconda.spec b/anaconda.spec index 603fcc234..30e20ee60 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -190,6 +190,7 @@ update-desktop-database &> /dev/null || : %doc docs/kickstart-docs.txt %doc docs/mediacheck.txt %doc docs/anaconda-release-notes.txt +/lib/udev/rules.d/70-anaconda.rules %{_bindir}/mini-wm %{_sbindir}/anaconda %ifarch i386 i486 i586 i686 x86_64 diff --git a/scripts/upd-instroot b/scripts/upd-instroot index 8491bc003..8e86eae7f 100755 --- a/scripts/upd-instroot +++ b/scripts/upd-instroot @@ -950,7 +950,6 @@ mv $DEST/usr/sbin/anaconda $DEST/usr/bin/anaconda mv $DEST/usr/lib/anaconda-runtime/lib* $DEST/usr/$LIBDIR 2>/dev/null mv $DEST/etc/yum.repos.d $DEST/etc/anaconda.repos.d -cp $DEST/usr/lib/anaconda-runtime/70-anaconda.rules $DEST/lib/udev/rules.d/ rm -f $DEST/usr/$LIBDIR/libunicode-lite* -- cgit From b2647ae85cfc8b7f03b867654abcdc9869caac71 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 16 Mar 2009 14:15:52 +0100 Subject: Catch LVMErrors too when tearing down devices When tearing down devices in devicetree.populate() we may fail to teardown certain LV's because they can be mounted (in the livecd case). --- storage/devicetree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index a2d038679..8ae91ee60 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1332,7 +1332,7 @@ class DeviceTree(object): for device in self.leaves: try: device.teardown(recursive=True) - except (DeviceError, DeviceFormatError) as e: + except (DeviceError, DeviceFormatError, LVMError) as e: log.info("teardown of %s failed: %s" % (device.name, e)) def setupAll(self): -- cgit From d20417940f803410f3c668b2599400394d6a84ab Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 16 Mar 2009 15:13:12 +0100 Subject: devicetree: fix slave addition of incomplete dm / md devices The code to add all slaves to the tree for a (potential) incomplete devicemapper or mdraid device, has 2 bugs: 1) It resolves a dm-x to a devicemapper name and then looks for /sys/class/block/foo/slaves/devicemapper-name instead of: /sys/class/block/foo/slaves/dm-x 2) It checks to see if the device was added by adding the slaves after adding the first slave instead of after adding all the slaves. This patch fixes both. --- storage/devicetree.py | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index 8ae91ee60..765c8571d 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -864,8 +864,10 @@ class DeviceTree(object): for slave_name in slave_names: # if it's a dm-X name, resolve it to a map name first if slave_name.startswith("dm-"): - slave_name = dm.name_from_dm_node(slave_name) - slave_dev = self.getDeviceByName(slave_name) + dev_name = dm.name_from_dm_node(slave_name) + else: + dev_name = slave_name + slave_dev = self.getDeviceByName(dev_name) if slave_dev: slaves.append(slave_dev) else: @@ -874,13 +876,15 @@ class DeviceTree(object): new_info = udev_get_block_device(os.path.realpath(path)) if new_info: self.addUdevDevice(new_info) - device = self.getDeviceByName(name) - if device is None: - # if the current device is still not in + if self.getDeviceByName(dev_name) is None: + # if the current slave is still not in # the tree, something has gone wrong - log.error("failure scanning device %s" % name) + log.error("failure scanning device %s: could not add slave %s" % (name, dev_name)) return + # try to get the device again now that we've got all the slaves + device = self.getDeviceByName(name) + if device is None and \ udev_device_is_dmraid_partition(info, self): diskname = udev_device_get_dmraid_partition_disk(info) @@ -919,8 +923,10 @@ class DeviceTree(object): for slave_name in slave_names: # if it's a dm-X name, resolve it to a map name if slave_name.startswith("dm-"): - slave_name = dm.name_from_dm_node(slave_name) - slave_dev = self.getDeviceByName(slave_name) + dev_name = dm.name_from_dm_node(slave_name) + else: + dev_name = slave_name + slave_dev = self.getDeviceByName(dev_name) if slave_dev: slaves.append(slave_dev) else: @@ -929,13 +935,15 @@ class DeviceTree(object): new_info = udev_get_block_device(os.path.realpath(path)) if new_info: self.addUdevDevice(new_info) - device = self.getDeviceByName(name) - if device is None: - # if the current device is still not in + if self.getDeviceByName(dev_name) is None: + # if the current slave is still not in # the tree, something has gone wrong - log.error("failure scanning device %s" % name) + log.error("failure scanning device %s: could not add slave %s" % (name, dev_name)) return + # try to get the device again now that we've got all the slaves + device = self.getDeviceByName(name) + # if we get here, we found all of the slave devices and # something must be wrong -- if all of the slaves we in # the tree, this device should be as well -- cgit From 0a1fac5adfe574b5b685e400dd451c998c6b0065 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Mon, 16 Mar 2009 15:30:23 +0100 Subject: Ignore loop and ram devices Ignore loop and ram devices, we normally already skip these in udev.py: enumerate_block_devices(), but we can still end up trying to add them to the tree when they are slaves of other devices, this happens for example with the livecd --- storage/devicetree.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/storage/devicetree.py b/storage/devicetree.py index 765c8571d..fdcac7c9e 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -816,6 +816,13 @@ class DeviceTree(object): if disk.name == os.path.basename(os.path.dirname(sysfs_path)): return True + # Ignore loop and ram devices, we normally already skip these in + # udev.py: enumerate_block_devices(), but we can still end up trying + # to add them to the tree when they are slaves of other devices, this + # happens for example with the livecd + if name.startswith("loop") or name.startswith("ram"): + return True + # FIXME: check for virtual devices whose slaves are on the ignore list def addUdevDevice(self, info): -- cgit From 817c01c381ee8961e50855d4e8b611fb73e1b14d Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 16 Mar 2009 16:08:05 -0400 Subject: When creating free space, handle cases other than clearpart --drives= --- storage/partitioning.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index f4c40f2f8..0fdd6b9b8 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -44,7 +44,10 @@ def _createFreeSpacePartitions(anaconda): # get a list of disks that have at least one free space region of at # least 100MB disks = [] - for disk in [d for d in anaconda.id.storage.disks if d.name in anaconda.id.storage.clearPartDisks]: + for disk in anaconda.id.storage.disks: + if anaconda.id.storage.clearPartDisks and disk not in anaconda.id.storage.clearPartDisks: + continue + partedDisk = disk.partedDisk part = disk.partedDisk.getFirstPartition() while part: @@ -161,7 +164,7 @@ def doAutoPartition(anaconda): disks = [] devs = [] - if anaconda.id.storage.doAutoPart or anaconda.isKickstart: + if anaconda.id.storage.doAutoPart and not anaconda.isKickstart: # kickstart uses clearPartitions even without autopart clearPartitions(anaconda.id.storage) -- cgit From ec6105c566396a346648270ad520eba565f5ad83 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Mon, 16 Mar 2009 15:09:48 -1000 Subject: Create a Makefile target to generate updates.img automatically. Based on what's changed in the repo since the last release tag. Copy everything necessary for the updates.img in './updates-img' and pack it up. Got tired of having my own local scripts to do this. LIMITATIONS: Will not copy in dependent libraries or figure out if a new _isys.so needs to be built. Pass KEEP=y if you want the updates staging directory kept so you can dump other things in to it. Run 'make updates KEEP=y' over and over to supplement your updates image. --- Makefile | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/Makefile b/Makefile index dd45b29af..b2a75c75b 100644 --- a/Makefile +++ b/Makefile @@ -169,3 +169,38 @@ bumpver: install-buildrequires: yum install $$(grep BuildRequires: anaconda.spec | cut -d ' ' -f 2) + +# Generate an updates.img based on the changed files since the release +# was tagged. Updates are copied to ./updates-img and then the image is +# created. By default, the updates subdirectory is removed after the +# image is made, but if you want to keep it around, run: +# make updates.img KEEP=y +# And since shell is both stupid and amusing, I only match the first +# character to be a 'y' or 'Y', so you can do: +# make updates.img KEEP=yosemite +# Ahh, shell. +updates: + @if [ ! -d updates-img ]; then \ + mkdir updates-img ; \ + fi ; \ + git diff --stat $(ARCHIVE_TAG) | grep " | " | \ + grep -v "^\ loader\/" | grep -v "\.spec" | grep -v "Makefile" | \ + grep -v "^\ po\/" | grep -v "^\ scripts\/" | \ + while read sourcefile stuff ; do \ + dn="$$(dirname $$sourcefile)" ; \ + case $$dn in \ + installclasses|storage|booty) \ + cp -a $$dn updates-img ; \ + find updates-img/$$dn -type f | grep Makefile | xargs rm -f ;; \ + *) \ + cp -a $$sourcefile updates-img ;; \ + esac ; \ + done ; \ + cd updates-img ; \ + echo -n "Creating updates.img..." ; \ + ( find . | cpio -c -o | gzip -9c ) > ../updates.img ; \ + cd .. ; \ + keep="$$(echo $(KEEP) | cut -c1 | tr [a-z] [A-Z])" ; \ + if [ ! "$$keep" = "Y" ]; then \ + rm -rf updates-imgs ; \ + fi -- cgit From f80193a0f79705f53da38205aa3b727144c6fb84 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Mon, 16 Mar 2009 15:49:57 -1000 Subject: Check for disk name being in disk.name not in clearPartDisks disk -> disk.name --- storage/partitioning.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index 0fdd6b9b8..de38e1ba4 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -45,7 +45,8 @@ def _createFreeSpacePartitions(anaconda): # least 100MB disks = [] for disk in anaconda.id.storage.disks: - if anaconda.id.storage.clearPartDisks and disk not in anaconda.id.storage.clearPartDisks: + if anaconda.id.storage.clearPartDisks and \ + (disk.name not in anaconda.id.storage.clearPartDisks): continue partedDisk = disk.partedDisk -- cgit From 2686f859a7fca477b0915ff02a1e5bb4d9a2dc8e Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 16 Mar 2009 17:36:45 -0500 Subject: Fix inconsistency in variable use in search for free space. --- storage/partitioning.py | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index de38e1ba4..e3dc81736 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -630,13 +630,13 @@ def allocatePartitions(disks, partitions): log.debug("allocating partition: %s ; disks: %s ; boot: %s ; primary: %s ; size: %dMB ; grow: %s ; max_size: %s" % (_part.name, req_disks, _part.req_bootable, _part.req_primary, _part.req_size, _part.req_grow, _part.req_max_size)) free = None use_disk = None + part_type = None # loop through disks for _disk in req_disks: disk = partedDisks[_disk.path] #for p in disk.partitions: # log.debug("disk %s: part %s" % (disk.device.path, p.path)) sectorSize = disk.device.physicalSectorSize - part_type = parted.PARTITION_NORMAL best = None # TODO: On alpha we are supposed to reserve either one or two @@ -651,36 +651,36 @@ def allocatePartitions(disks, partitions): log.debug("no free partition slots on %s" % _disk.name) continue - if _part.req_primary and part_type != parted.PARTITION_NORMAL: + if _part.req_primary and new_part_type != parted.PARTITION_NORMAL: # we need a primary slot and none are free on this disk log.debug("no primary slots available on %s" % _disk.name) continue best = getBestFreeSpaceRegion(disk, - part_type, + new_part_type, _part.req_size, best_free=free, boot=_part.req_bootable) if best == free and not _part.req_primary and \ - part_type == parted.PARTITION_NORMAL: + new_part_type == parted.PARTITION_NORMAL: # see if we can do better with a logical partition log.debug("not enough free space for primary -- trying logical") - part_type = getNextPartitionType(disk, no_primary=True) - if part_type: - free = getBestFreeSpaceRegion(disk, - part_type, + new_part_type = getNextPartitionType(disk, no_primary=True) + if new_part_type: + best = getBestFreeSpaceRegion(disk, + new_part_type, _part.req_size, best_free=free, boot=_part.req_bootable) - elif best: - if free != best: - # now we know we are choosing a new free space, - # so update the disk and part type - log.debug("updating use_disk to %s (%s), type: %s" - % (_disk, _disk.name, new_part_type)) - part_type = new_part_type - use_disk = _disk + + if best and free != best: + # now we know we are choosing a new free space, + # so update the disk and part type + log.debug("updating use_disk to %s (%s), type: %s" + % (_disk, _disk.name, new_part_type)) + part_type = new_part_type + use_disk = _disk log.debug("new free: %s (%d-%d / %dMB)" % (best, best.start, best.end, -- cgit From 876366fa90a7d01da9c72c54b0e71433d3fecfee Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 16 Mar 2009 17:37:56 -0500 Subject: Fix bug in dependency list for partitions. A change at some point removed the important clause that the 'dep' partition must be extended in order to make all logical partitions depend on it. --- storage/devicetree.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index fdcac7c9e..28d23c8f4 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -766,7 +766,8 @@ class DeviceTree(object): # special handling for extended partitions since the logical # partitions and their deps effectively depend on the extended logicals = [] - if isinstance(dep, PartitionDevice): + if isinstance(dep, PartitionDevice) and dep.partType and \ + dep.isExtended: # collect all of the logicals on the same disk for part in self.getDevicesByInstance(PartitionDevice): if part.partType and part.isLogical and part.disk == dep.disk: -- cgit From 78c68671e51d983f9400ee138deea861210f920a Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 16 Mar 2009 17:39:53 -0500 Subject: Fix handling of new extended partitions during partition allocation. First, we were trying to remove the extended before removing the logicals from it. Second, we were treating the implicit extended request as if it were a real request. --- storage/partitioning.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/storage/partitioning.py b/storage/partitioning.py index e3dc81736..c10a3d2eb 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -603,6 +603,9 @@ def allocatePartitions(disks, partitions): log.debug("removing all non-preexisting from disk(s)") for _part in new_partitions: if _part.partedPartition: + if _part.isExtended: + # these get removed last + continue #_part.disk.partedDisk.removePartition(_part.partedPartition) partedDisk = partedDisks[_part.disk.partedDisk.device.path] #log.debug("removing part %s (%s) from disk %s (%s)" % (_part.partedPartition.path, [p.path for p in _part.partedPartition.disk.partitions], partedDisk.device.path, [p.path for p in partedDisk.partitions])) @@ -615,6 +618,10 @@ def allocatePartitions(disks, partitions): partedDisk.removePartition(extended) for _part in new_partitions: + if _part.partedPartition and _part.isExtended: + # ignore new extendeds as they are implicit requests + continue + # obtain the set of candidate disks req_disks = [] if _part.disk: -- cgit From 4b666dfd4710c255874a4c8ccdea919ced910981 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 16 Mar 2009 17:42:12 -0500 Subject: Do not create a LUKSDevice if we do not have a way to map the device. If the user elects not to provide a passphrase or key, do not create a LUKSDevice. We will just keep the backing device with its luks format. This saves us the trouble of checking whether we have the ability to set up each LUKSDevice since we only have LUKSDevice instances for those we can set up. --- iw/partition_dialog_gui.py | 17 +++++++++++++---- iw/partition_gui.py | 24 ++++++++++++++++++------ iw/raid_dialog_gui.py | 34 +++++++++++++++++++++++++--------- storage/devicetree.py | 4 +++- 4 files changed, 59 insertions(+), 20 deletions(-) diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index 802161648..5abbd512c 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -180,11 +180,15 @@ class PartitionEditor: elif self.lukscb and not self.lukscb.get_active() and \ self.origrequest.format.type == "luks": # destroy the luks format and the mapped device - luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] - if luksdev: + try: + luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] + except IndexError: + pass + else: actions.append(ActionDestroyFormat(luksdev)) actions.append(ActionDestroyDevice(luksdev)) luksdev = None + actions.append(ActionDestroyFormat(request)) if self.isNew: @@ -282,8 +286,13 @@ class PartitionEditor: # if this is a luks device we need to grab info from two devices # to make it seem like one device. wee! if self.origrequest.format.type == "luks": - luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] - usereq = luksdev + try: + luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] + except IndexError: + usereq = self.origrequest + luksdev = None + else: + usereq = luksdev else: luksdev = None usereq = self.origrequest diff --git a/iw/partition_gui.py b/iw/partition_gui.py index c7097b0c2..668530c81 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -697,8 +697,12 @@ class PartitionWindow(InstallWindow): if lv.format.type == "luks": # we'll want to grab format info from the mapped # device, not the encrypted one - dm_dev = self.storage.devicetree.getChildren(lv)[0] - format = dm_dev.format + try: + dm_dev = self.storage.devicetree.getChildren(lv)[0] + except IndexError: + format = lv.format + else: + format = dm_dev.format else: format = lv.format iter = self.tree.append(vgparent) @@ -732,8 +736,12 @@ class PartitionWindow(InstallWindow): if array.format.type == "luks": # look up the mapped/decrypted device since that's # where we'll find the format we want to display - dm_dev = self.storage.getChildren(array)[0] - format = dm_dev.format + try: + dm_dev = self.storage.getChildren(array)[0] + except IndexError: + format = array.format + else: + format = dm_dev.format else: format = array.format @@ -840,8 +848,12 @@ class PartitionWindow(InstallWindow): if device and device.format.type == "luks": # look up the mapped/decrypted device in the tree # the format we care about will be on it - dm_dev = self.storage.devicetree.getChildren(device)[0] - format = dm_dev.format + try: + dm_dev = self.storage.devicetree.getChildren(device)[0] + except IndexError: + format = device.format + else: + format = dm_dev.format elif device: format = device.format else: diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index 3ae2f6e09..cab2bb067 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -187,13 +187,16 @@ class RaidEditor: not self.fsoptionsDict["lukscb"].get_active() and \ self.origrequest.format.type == "luks": # destroy luks format and mapped device - luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] - if luksdev: + try: + luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] + except IndexError: + pass + else: actions.append(ActionDestroyFormat(luksdev.format)) actions.append(ActionDestroyDevice(luksdev)) luksdev = None - actions.append(ActionDestroyFormat(self.origrequest)) + actions.append(ActionDestroyFormat(self.origrequest)) else: # existing device fmt_class = self.fsoptionsDict["fstypeCombo"].get_active_value() @@ -214,11 +217,15 @@ class RaidEditor: not self.fsoptionsDict["lukscb"].get_active() and \ self.origrequest.format.type == "luks": # destroy luks format and mapped device - luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] - if luksdev: + try: + luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] + except IndexError: + pass + else: actions.append(ActionDestroyFormat(luksdev.format)) actions.append(ActionDestroyDevice(luksdev)) luksdev = None + actions.append(ActionDestroyFormat(self.origrequest)) elif self.origrequest.format.mountable: self.origrequest.format.mountpoint = mountpoint @@ -226,7 +233,10 @@ class RaidEditor: if self.fsoptionsDict.has_key("migratecb") and \ self.fsoptionsDict["migratecb"].get_active(): if origrequest.format.type == "luks": - usedev = self.storage.devicetree.getChildren(origrequest)[0] + try: + usedev = self.storage.devicetree.getChildren(origrequest)[0] + except IndexError: + usedev = origrequest else: usedev = origrequest migrate = True @@ -332,9 +342,15 @@ class RaidEditor: self.lukscb.set_data("formatstate", 1) if origrequest.format.type == "luks": - luksdev = self.storage.devicetree.getChildren(origrequest)[0] - usedev = luksdev - format = usedev.format + try: + luksdev = self.storage.devicetree.getChildren(origrequest)[0] + except IndexError: + luksdev = None + usedev = origrequest + format = origrequest.format + else: + usedev = luksdev + format = usedev.format else: luksdev = None usedev = origrequest diff --git a/storage/devicetree.py b/storage/devicetree.py index 28d23c8f4..b015b551e 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1127,12 +1127,14 @@ class DeviceTree(object): luks_device = LUKSDevice(device.format.mapName, parents=[device], exists=True) - self._addDevice(luks_device) try: luks_device.setup() except (LUKSError, CryptoError, DeviceError) as e: log.info("setup of %s failed: %s" % (format.mapName, e)) + device.removeChild() + else: + self._addDevice(luks_device) else: log.warning("luks device %s already in the tree" % format.mapName) -- cgit From 9db089bf2338d749375d9a41ec7bb5c9efd5fa0b Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 16 Mar 2009 17:45:54 -0500 Subject: Reset encryptionPassphrase when we reset the rest of storage. Any devices we have keys for already have their key saved in a hash based on the LUKS UUID, so we will not lose information. We are only resetting the global passphrase used for newly created devices. --- storage/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/__init__.py b/storage/__init__.py index 416b4eea2..cd99ca30b 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -207,6 +207,7 @@ class Storage(object): """ # save passphrases for luks devices so we don't have to reprompt + self.encryptionPassphrase = None for device in self.devices: if device.format.type == "luks" and device.format.exists: self.__luksDevs[device.format.uuid] = device.format._LUKS__passphrase -- cgit From e23392f8d4aba295ce67f06193ff0bacc11da6e9 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 16 Mar 2009 17:48:17 -0500 Subject: Only schedule implicit format destruction if there is formatting to destroy. No need to schedule unneeded actions for devices that are initially clean. --- storage/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/__init__.py b/storage/__init__.py index cd99ca30b..84ce93e88 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -575,7 +575,7 @@ class Storage(object): def destroyDevice(self, device): """ Schedule destruction of a device. """ - if device.format.exists: + if device.format.exists and device.format.type: # schedule destruction of any formatting while we're at it self.devicetree.registerAction(ActionDestroyFormat(device)) -- cgit From aff13e4bd26175345ca0545227916f6af40411eb Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 16 Mar 2009 21:49:31 -0500 Subject: Don't use disk.maximizePartition anymore. We are doing a fine job of maximizing things on our own, and maximizePartitions is actually shrinking our partitions in an effort to make sure they are cylinder-aligned. Bottom line, it's not doing anything useful. If we decide to start making all partitions cylinder-aligned, we will want to do it in another way besides calling maximizePartitions, and presumably our current accuracy will be preserved. If not, we can always bring it back. --- storage/partitioning.py | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index c10a3d2eb..8243080de 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -932,29 +932,6 @@ def growPartitions(disks, partitions): except PartitioningError, e: raise PartitioningError("failed to grow partitions") - # Maximize partitions, we do this after growing all partitions - # as some partitions may grow unlimited, and we don't want them - # eating up the entire disk when we still need to grow others - for part in growable: - constraint = parted.Constraint(device=disk.partedDisk.device) - - # don't grow beyond the request's maximum size - if part.req_max_size: - max_sect = (part.req_max_size * (1024 * 1024)) / sectorSize - if constraint.maxSize > max_sect: - constraint.maxSize = max_sect - - # don't grow beyond the resident filesystem's max size - if part.format.maxSize > 0: - max_sect = (part.format.maxSize * (1024 * 1024)) / sectorSize - if constraint.maxSize > max_sect: - constraint.maxSize = max_sect - - disk.partedDisk.maximizePartition(part.partedPartition, constraint) - log.debug("grew partition %s to %dMB" % (part.name, - part.partedPartition.getSize())) - log.debug("the disk's copy is %dMB" % disk.partedDisk.getPartitionByPath(part.path).getSize()) - # reset all requests to their original requested size for part in partitions: if part.exists: -- cgit From 792b6b29c7415a12f4d62856721358b62dcad4da Mon Sep 17 00:00:00 2001 From: Martin Gracik Date: Fri, 13 Mar 2009 14:58:19 +0100 Subject: Added the run_test.py script to easily run the test cases. Some small changes to test cases were needed. Added suite() function to them and changed the class names. Also __init__.py files were added to the directories. --- run_test.py | 108 ++++++++++++++++++++++++++++++++++ tests/__init__.py | 29 +++++++++ tests/storage/__init__.py | 0 tests/storage/devicelibs/__init__.py | 0 tests/storage/devicelibs/baseclass.py | 76 ++++++++++++------------ tests/storage/devicelibs/crypto.py | 11 ++-- tests/storage/devicelibs/lvm.py | 18 +++--- tests/storage/devicelibs/mdraid.py | 12 ++-- tests/storage/devicelibs/swap.py | 11 ++-- 9 files changed, 207 insertions(+), 58 deletions(-) create mode 100755 run_test.py create mode 100644 tests/__init__.py create mode 100644 tests/storage/__init__.py create mode 100644 tests/storage/devicelibs/__init__.py diff --git a/run_test.py b/run_test.py new file mode 100755 index 000000000..fd24fa972 --- /dev/null +++ b/run_test.py @@ -0,0 +1,108 @@ +#!/usr/bin/env python +#-*- coding:utf-8 -*- + +import sys + +REQUIRED_PATHS = ["/usr/lib/anaconda", + "/usr/share/system-config-date"] +sys.path.extend(REQUIRED_PATHS) + +import unittest +import tests +import string +from optparse import OptionParser + + +def getFullTestName(test, full_test_names): + tests = [] + for full_test_name in full_test_names: + if full_test_name.lower().find(test) != -1: + tests.append(full_test_name) + + return tests + + +if __name__ == "__main__": + usage = "usage: %prog [options] [test1 test2 ...]" + parser = OptionParser(usage) + parser.add_option("-l", "--list", action="store_true", default=False, + help="print all available tests and exit") + + (options, args) = parser.parse_args(sys.argv[1:]) + + print "Searching for test suites" + available_suites = tests.getAvailableSuites() + test_keys = available_suites.keys() + if not test_keys: + print "No test suites available, exiting" + sys.exit(1) + + test_keys.sort() + + if options.list: + print "\nAvailable tests:" + for test_name in test_keys: + print test_name + sys.exit(0) + + tests_to_run = [] + + if len(args) == 0: + # interactive mode + print "Running in interactive mode" + print "\nAvailable tests:" + test_num = 0 + for test_name in test_keys: + print "[%3d] %s" % (test_num, test_name) + test_num += 1 + print + + try: + input_string = raw_input("Type in the test you want to run, " + "or \"all\" to run all listed tests: ") + except KeyboardInterrupt as e: + print "\nAborted by user" + sys.exit(1) + + for arg in input_string.split(): + if arg.isdigit(): + arg = int(arg) + try: + args.append(test_keys[arg]) + except KeyError as e: + pass + else: + args.append(arg) + + args = map(string.lower, args) + if "all" in args: + tests_to_run = test_keys[:] + else: + for arg in args: + matching_tests = getFullTestName(arg, test_keys) + tests_to_run.extend(filter(lambda test: test not in tests_to_run, + matching_tests)) + + # run the tests + if tests_to_run: + tests_to_run.sort() + print "Running tests: %s" % tests_to_run + test_suite = unittest.TestSuite([available_suites[test] + for test in tests_to_run]) + + try: + result = unittest.TextTestRunner(verbosity=2).run(test_suite) + except KeyboardInterrupt as e: + print "\nAborted by user" + sys.exit(1) + + if result.wasSuccessful(): + print "\nAll tests OK" + sys.exit(0) + else: + print "\nTests finished with %d errors and %d failures" % (len(result.errors), + len(result.failures)) + sys.exit(2) + else: + print "No test suites matching your criteria found, exiting" + sys.exit(1) diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 000000000..d5b53a8af --- /dev/null +++ b/tests/__init__.py @@ -0,0 +1,29 @@ +import os + +# this has to be imported before running anything +import anaconda_log +import upgrade + + +def getAvailableSuites(): + root, tests_dir = os.path.split(os.path.dirname(__file__)) + modules = [] + + for root, dirs, files in os.walk(tests_dir): + for filename in files: + if filename.endswith(".py") and filename != "__init__.py": + basename, extension = os.path.splitext(filename) + modules.append(os.path.join(root, basename).replace("/", ".")) + + available_suites = {} + for module in modules: + imported = __import__(module, globals(), locals(), [module], -1) + try: + suite = getattr(imported, "suite") + except AttributeError as e: + continue + + if callable(suite): + available_suites[module] = suite() + + return available_suites diff --git a/tests/storage/__init__.py b/tests/storage/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/storage/devicelibs/__init__.py b/tests/storage/devicelibs/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/tests/storage/devicelibs/baseclass.py b/tests/storage/devicelibs/baseclass.py index 01fe6c779..c19bfc376 100644 --- a/tests/storage/devicelibs/baseclass.py +++ b/tests/storage/devicelibs/baseclass.py @@ -2,11 +2,44 @@ import unittest import os import subprocess -# need to import these first before doing anything -import anaconda_log -import upgrade -class TestDevicelibs(unittest.TestCase): +def makeLoopDev(device_name, file_name): + proc = subprocess.Popen(["dd", "if=/dev/zero", "of=%s" % file_name, + "bs=1024", "count=102400"], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + while True: + proc.communicate() + if proc.returncode is not None: + rc = proc.returncode + break + if rc: + raise OSError, "dd failed creating the file %s" % file_name + + proc = subprocess.Popen(["losetup", device_name, file_name], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + while True: + proc.communicate() + if proc.returncode is not None: + rc = proc.returncode + break + if rc: + raise OSError, "losetup failed setting up the loop device %s" % device_name + +def removeLoopDev(device_name, file_name): + proc = subprocess.Popen(["losetup", "-d", device_name], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + while True: + proc.communicate() + if proc.returncode is not None: + rc = proc.returncode + break + if rc: + raise OSError, "losetup failed removing the loop device %s" % device_name + + os.unlink(file_name) + + +class DevicelibsTestCase(unittest.TestCase): _LOOP_DEVICES = (("/dev/loop0", "/tmp/test-virtdev0"), ("/dev/loop1", "/tmp/test-virtdev1")) @@ -15,39 +48,8 @@ class TestDevicelibs(unittest.TestCase): def setUp(self): for dev, file in self._LOOP_DEVICES: - self.makeLoopDev(dev, file) + makeLoopDev(dev, file) def tearDown(self): for dev, file in self._LOOP_DEVICES: - self.removeLoopDev(dev, file) - - def makeLoopDev(self, device_name, file_name): - proc = subprocess.Popen(["dd", "if=/dev/zero", "of=%s" % file_name, "bs=1024", "count=102400"]) - while True: - proc.communicate() - if proc.returncode is not None: - rc = proc.returncode - break - if rc: - raise OSError, "dd failed creating the file %s" % file_name - - proc = subprocess.Popen(["losetup", device_name, file_name]) - while True: - proc.communicate() - if proc.returncode is not None: - rc = proc.returncode - break - if rc: - raise OSError, "losetup failed setting up the loop device %s" % device_name - - def removeLoopDev(self, device_name, file_name): - proc = subprocess.Popen(["losetup", "-d", device_name]) - while True: - proc.communicate() - if proc.returncode is not None: - rc = proc.returncode - break - if rc: - raise OSError, "losetup failed removing the loop device %s" % device_name - - os.remove(file_name) + removeLoopDev(dev, file) diff --git a/tests/storage/devicelibs/crypto.py b/tests/storage/devicelibs/crypto.py index 6a76569c5..0f9f7bdb5 100644 --- a/tests/storage/devicelibs/crypto.py +++ b/tests/storage/devicelibs/crypto.py @@ -5,9 +5,9 @@ import storage.devicelibs.crypto as crypto import tempfile import os -class TestCrypto(baseclass.TestDevicelibs): +class CryptoTestCase(baseclass.DevicelibsTestCase): - def runTest(self): + def testCrypto(self): ## ## is_luks ## @@ -118,6 +118,9 @@ class TestCrypto(baseclass.TestDevicelibs): os.unlink(new_keyfile) +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(CryptoTestCase) + + if __name__ == "__main__": - suite = unittest.TestLoader().loadTestsFromTestCase(TestCrypto) - unittest.TextTestRunner(verbosity=2).run(suite) + unittest.main() diff --git a/tests/storage/devicelibs/lvm.py b/tests/storage/devicelibs/lvm.py index 62c52d168..e6ba1a628 100644 --- a/tests/storage/devicelibs/lvm.py +++ b/tests/storage/devicelibs/lvm.py @@ -2,9 +2,9 @@ import baseclass import unittest import storage.devicelibs.lvm as lvm -class TestLVM(baseclass.TestDevicelibs): +class LVMTestCase(baseclass.DevicelibsTestCase): - def testLVMStuff(self): + def testLVM(self): ## ## pvcreate ## @@ -154,7 +154,7 @@ class TestLVM(baseclass.TestDevicelibs): self.assertEqual(lvm.has_lvm(), True) # fail - #TODO + # TODO ## ## lvremove @@ -214,15 +214,17 @@ class TestLVM(baseclass.TestDevicelibs): self.assertEqual(lvm.clampSize(10, 4, True), 12L) #def testVGUsedSpace(self): - #TODO + # TODO pass #def testVGFreeSpace(self): - #TODO + # TODO pass -if __name__ == "__main__": - suite = unittest.TestLoader().loadTestsFromTestCase(TestLVM) - unittest.TextTestRunner(verbosity=2).run(suite) +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(LVMTestCase) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/storage/devicelibs/mdraid.py b/tests/storage/devicelibs/mdraid.py index 6e49e55b0..3c0ee7224 100644 --- a/tests/storage/devicelibs/mdraid.py +++ b/tests/storage/devicelibs/mdraid.py @@ -4,9 +4,9 @@ import storage.devicelibs.mdraid as mdraid import time -class TestMDRaid(baseclass.TestDevicelibs): +class MDRaidTestCase(baseclass.DevicelibsTestCase): - def testMDRaidStuff(self): + def testMDRaid(self): ## ## getRaidLevels ## @@ -99,7 +99,9 @@ class TestMDRaid(baseclass.TestDevicelibs): self.assertRaises(mdraid.MDRaidError, mdraid.mddestroy, "/not/existing/device") -if __name__ == "__main__": - suite = unittest.TestLoader().loadTestsFromTestCase(TestMDRaid) - unittest.TextTestRunner(verbosity=2).run(suite) +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(MDRaidTestCase) + +if __name__ == "__main__": + unittest.main() diff --git a/tests/storage/devicelibs/swap.py b/tests/storage/devicelibs/swap.py index b1e9bd3b5..b99d1f67c 100644 --- a/tests/storage/devicelibs/swap.py +++ b/tests/storage/devicelibs/swap.py @@ -2,9 +2,9 @@ import baseclass import unittest import storage.devicelibs.swap as swap -class TestSwap(baseclass.TestDevicelibs): +class SwapTestCase(baseclass.DevicelibsTestCase): - def runTest(self): + def testSwap(self): ## ## mkswap ## @@ -58,6 +58,9 @@ class TestSwap(baseclass.TestDevicelibs): self.assertRaises(swap.SwapError, swap.swapoff, self._LOOP_DEV0) +def suite(): + return unittest.TestLoader().loadTestsFromTestCase(SwapTestCase) + + if __name__ == "__main__": - suite = unittest.TestLoader().loadTestsFromTestCase(TestSwap) - unittest.TextTestRunner(verbosity=2).run(suite) + unittest.main() -- cgit From f65fe49797f06761c3d8cbb074cbedf8b00a38a2 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Mon, 16 Mar 2009 17:16:54 -1000 Subject: New version. --- anaconda.spec | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 49 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index 30e20ee60..a376a9b68 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.30 +Version: 11.5.0.31 Release: 1 License: GPLv2+ Group: Applications/System @@ -210,6 +210,54 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Mon Mar 16 2009 David Cantrell - 11.5.0.31-1 +- Don't use disk.maximizePartition anymore. (dlehman) +- Only schedule implicit format destruction if there is formatting to + destroy. (dlehman) +- Reset encryptionPassphrase when we reset the rest of storage. (dlehman) +- Do not create a LUKSDevice if we do not have a way to map the device. + (dlehman) +- Fix handling of new extended partitions during partition allocation. + (dlehman) +- Fix bug in dependency list for partitions. (dlehman) +- Fix inconsistency in variable use in search for free space. (dlehman) +- Check for disk name being in disk.name not in clearPartDisks (dcantrell) +- Create a Makefile target to generate updates.img automatically. (dcantrell) +- When creating free space, handle cases other than clearpart --drives= + (clumens) +- Ignore loop and ram devices (hdegoede) +- devicetree: fix slave addition of incomplete dm / md devices (hdegoede) +- Catch LVMErrors too when tearing down devices (hdegoede) +- Install udev rules in /lib/udev/rules.d instead of in runtime dir + (hdegoede) +- Ignore disk devices with missing media (#488800). (clumens) +- Use correct parse method for the upgrade command (#471232) (wwoods) +- Fix creation of fs options for preexisting encrypted devices. (dlehman) +- Fix lots of buggy behavior in the partition dialog. (dlehman) +- Handle FTP servers that both want and don't want PASS after USER + (#490350). (clumens) +- Fixed the names of the variables for lvm.py functions. (mgracik) +- editPartitionRequest -> editPartition in iw/partition_gui.py (#490384). + (clumens) +- clampPVSize -> clampSize in lvm.py (#490295). (clumens) +- Fix the obvious and stupid typo (#490296). (clumens) +- isys.umount removes mount directory by default (rvykydal) +- Fix tempfile.mkdtemp call. (rvykydal) +- Initialize attribute _mountpoint before using it (rvykydal) +- devicetree.py has _ignoredDisks instead of ignoredDisks. (jgranado) +- Create separate resize actions for formats and devices. (dcantrell) +- Use os.statvfs() to get existing filesystem size. (dcantrell) +- Add resizeArgs for Ext2FS and fix it for BtrFS. (dcantrell) +- Report when we cannot find any free space partitions. (dcantrell) +- Improve resizeDialog text. (dcantrell) +- Raise FSResizeError if filesystem cannot be resized. (dcantrell) +- Handle resizing when setting targetSize for PartitionDevice (dcantrell) +- Let users set the size property of StorageDevices. (dcantrell) +- Add support for kickstart's '--initlabel' option to clearpart. (dlehman) +- Fix display of LV format type for encrypted LVs. (dlehman) +- Make paths somewhat flexible so we'll work in normal environments. + (dlehman) + * Fri Mar 13 2009 David Lehman - 11.5.0.30-1 - Fix supportable attribute for cmdline-enabled fstypes. (dlehman) - Access private attribute for luks dict. (dlehman) -- cgit From 0492bad33dd1b4b2002950efb9b1ac8f17e4cb4c Mon Sep 17 00:00:00 2001 From: Martin Gracik Date: Tue, 17 Mar 2009 13:50:36 +0100 Subject: Changed the getDevicebyLabel() to getDeviceByLabel() in devicetree.py Didn't see it used anywhere, so I hope it will not break any compatibility. All other functions have ...By... --- storage/devicetree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index b015b551e..3f8e0855d 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1382,7 +1382,7 @@ class DeviceTree(object): return found - def getDevicebyLabel(self, label): + def getDeviceByLabel(self, label): found = None for device in self._devices: _label = getattr(device.format, "label", None) -- cgit From 2538723c53378bfe2044aa9fbc466e8a0d940607 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 17 Mar 2009 10:08:55 -0400 Subject: storage.disks never includes disks without media present. --- booty/bootloaderInfo.py | 2 +- kickstart.py | 2 +- partedUtils.py | 2 +- storage/__init__.py | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index d1147348c..0c1ec5b5b 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -453,7 +453,7 @@ class bootloaderInfo: f.write("\n") def updateDriveList(self, sortedList=[]): - self._drivelist = map(lambda x: x.name, filter(lambda dev: dev.mediaPresent, self.storage.disks)) + self._drivelist = map(lambda x: x.name, self.storage.disks) self._drivelist.sort(isys.compareDrives) # If we're given a sort order, make sure the drives listed in it diff --git a/kickstart.py b/kickstart.py index 06f6199de..dfabd79b9 100644 --- a/kickstart.py +++ b/kickstart.py @@ -238,7 +238,7 @@ class ClearPart(commands.clearpart.FC3_ClearPart): if self.type is None: self.type = CLEARPART_TYPE_NONE - hds = map(lambda x: x.name, filter(lambda dev: dev.mediaPresent, self.handler.id.storage.disks)) + hds = map(lambda x: x.name, self.handler.id.storage.disks) for disk in self.drives: if disk not in hds: raise KickstartValueError, formatErrorMsg(self.lineno, msg="Specified nonexistent disk %s in clearpart command" % disk) diff --git a/partedUtils.py b/partedUtils.py index d3049eb7e..d0077f2b0 100644 --- a/partedUtils.py +++ b/partedUtils.py @@ -641,7 +641,7 @@ class DiskSet: def driveList (self): """Return the list of drives on the system.""" - drives = map(lambda x: x.name, filter(lambda x: isys.mediaPresent(x.name), self.anaconda.id.storage.disks)) + drives = map(lambda x: x.name, self.anaconda.id.storage.disks) drives.sort (isys.compareDrives) return drives diff --git a/storage/__init__.py b/storage/__init__.py index 84ce93e88..08ba90aed 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -614,7 +614,7 @@ class Storage(object): def extendedPartitionsSupported(self): """ Return whether any disks support extended partitions.""" - for disk in filter(lambda disk: disk.mediaPresent, self.disks): + for disk in self.disks: if disk.partedDisk.supportsFeature(parted.DISK_TYPE_EXTENDED): return True return False -- cgit From feba2676ea103d9c8ab0a89b26d213e7f17deaab Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 17 Mar 2009 10:37:29 -0400 Subject: Use minihal instead of isys.hardDriveDict in list-harddrives (#488122). list-harddrives was completely broken in the new storage world. Incidentally, this patch also fixes the referenced bug by removing all the unnecessary imports that were dragging in zonetab. --- command-stubs/list-harddrives-stub | 42 +++++++++----------------------------- 1 file changed, 10 insertions(+), 32 deletions(-) diff --git a/command-stubs/list-harddrives-stub b/command-stubs/list-harddrives-stub index a282984d6..ceb60368d 100755 --- a/command-stubs/list-harddrives-stub +++ b/command-stubs/list-harddrives-stub @@ -2,7 +2,7 @@ # # scan system for harddrives and output device name/size # -# Copyright (C) 2007 Red Hat, Inc. All rights reserved. +# Copyright (C) 2007, 2009 Red Hat, Inc. All rights reserved. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -18,42 +18,20 @@ # along with this program. If not, see . # -import os import sys -# for testing -if (os.path.exists('isys')): - sys.path.append('isys') - sys.path.append('/usr/lib/anaconda') -import anaconda_log -import parted -import partedUtils -import isys - -drives = isys.hardDriveDict() - -driveList = drives.keys() -driveList.sort() - -for drive in driveList: - if not isys.mediaPresent(drive): - continue - - # try to open and get size - skip = 0 - deviceFile = "/dev/%s" % (drive,) +import minihal - try: - dev = parted.PedDevice.get(deviceFile) - except: - skip = 1 +lst = [] - if skip: - continue +for drive in minihal.get_devices_by_type("volume"): + if not drive.has_key("device") or not drive.has_key("volume.size"): + continue - sizeMB = (float(dev.heads * dev.cylinders * dev.sectors) / (1024 * 1024) - * dev.sectorSize) + lst.append("%s %s" % (drive["device"], drive["volume_size"]/(1024*1024))) - print drive, sizeMB +lst.sort() +for entry in lst: + print lst -- cgit From 2a6e841778c7b75fa09e8396d0ad9777b0a6c0e0 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 17 Mar 2009 16:34:06 +0100 Subject: Get iscsi going with the new storage code This patch gets iscsi going with the new storage code. There are still a few hicups left (such as NetworkManager downing the interface our / is on), but I'll address those in separate patches. --- backend.py | 6 ++++-- isys/isys.py | 9 --------- storage/devices.py | 30 +++++++++--------------------- storage/devicetree.py | 15 ++++++++++++--- storage/iscsi.py | 40 ++++++++++++++-------------------------- storage/udev.py | 30 ++++++++++++++++++++++++++++++ yuminstall.py | 7 ------- 7 files changed, 69 insertions(+), 68 deletions(-) diff --git a/backend.py b/backend.py index 475785b12..680f05884 100644 --- a/backend.py +++ b/backend.py @@ -85,8 +85,10 @@ class AnacondaBackend: # the initrd might need iscsi-initiator-utils, and chances are # it was not installed yet the first time mkinitrd was run, as # mkinitrd does not require it. - for disk in anaconda.id.storage.disks: - if isys.driveIsIscsi(disk.path): + root = anaconda.id.storage.fsset.rootDevice + disks = anaconda.id.storage.devicetree.getDevicesByType("iscsi") + for disk in disks: + if root.dependsOn(disk): has_iscsi_disk = True break diff --git a/isys/isys.py b/isys/isys.py index 07c69a0a1..ee1866686 100755 --- a/isys/isys.py +++ b/isys/isys.py @@ -465,15 +465,6 @@ def driveUsesModule(device, modules): pass return rc -def driveIsIscsi(device): - # ewww. just ewww. - if not os.path.islink("/sys/block/%s/device" %(device,)): - return False - target = os.path.realpath("/sys/block/%s/device" %(device,)) - if re.search("/platform/host[0-9]*/session[0-9]*/target[0-9]*:[0-9]*:[0-9]*/[0-9]*:[0-9]*:[0-9]*:[0-9]*", target) is not None: - return True - return False - def vtActivate (num): if rhpl.getArch() == "s390": return diff --git a/storage/devices.py b/storage/devices.py index 397a648b0..c9d63fc42 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -2657,28 +2657,16 @@ class DirectoryDevice(FileDevice): class iScsiDiskDevice(DiskDevice): - """ An iSCSI volume/device/target/???. - - TODO: learn what this is and how we need to use it. - """ + """ An iSCSI disk. """ _type = "iscsi" - - def __init__(self, ipaddr, port, - user=None, passwd=None, - user_in=None, passwd_in=None, - major=None, minor=None, size=None, - exists=None, parents=None, sysfsPath=''): - name = "iscsi://%s:%s" % (ipaddr, port) - DiskDevice.__init__(self, name, size=size, - major=major, minor=minor, exists=exists, - parents=parents, sysfsPath=sysfsPath) - def __str__(self): - return "FIXME: Please add iScsiDiskDevice.__str__" - - def probe(self): - """ Probe for any missing information about this device. """ - raise NotImplementedError("probe method not defined for StorageDevice") - + _packages = ["iscsi-initiator-utils"] + + def __init__(self, device, **kwargs): + self.iscsi_name = kwargs.pop("iscsi_name") + self.iscsi_address = kwargs.pop("iscsi_address") + self.iscsi_port = int(kwargs.pop("iscsi_port")) + DiskDevice.__init__(self, device, **kwargs) + log.debug("created new iscsi disk %s %s:%d" % (self.iscsi_name, self.iscsi_address, self.iscsi_port)) class OpticalDevice(StorageDevice): """ An optical drive, eg: cdrom, dvd+r, &c. diff --git a/storage/devicetree.py b/storage/devicetree.py index 3f8e0855d..3a2af94c3 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -997,7 +997,16 @@ class DeviceTree(object): sysfsPath=sysfs_path, exists=True) self._addDevice(device) elif udev_device_is_disk(info): - log.debug("%s is a disk" % name) + kwargs = {} + if udev_device_is_iscsi(info): + diskType = iScsiDiskDevice + kwargs["iscsi_name"] = udev_device_get_iscsi_name(info) + kwargs["iscsi_address"] = udev_device_get_iscsi_address(info) + kwargs["iscsi_port"] = udev_device_get_iscsi_port(info) + log.debug("%s is an iscsi disk" % name) + else: + diskType = DiskDevice + log.debug("%s is a disk" % name) device = self.getDeviceByName(name) if device is None: try: @@ -1020,11 +1029,11 @@ class DeviceTree(object): else: initlabel = False - device = DiskDevice(name, + device = diskType(name, major=udev_device_get_major(info), minor=udev_device_get_minor(info), sysfsPath=sysfs_path, - initcb=cb, initlabel=initlabel) + initcb=cb, initlabel=initlabel, **kwargs) self._addDevice(device) except DeviceUserDeniedFormatError: #drive not initialized? self.addIgnoredDisk(name) diff --git a/storage/iscsi.py b/storage/iscsi.py index fff6ad14a..1add6eebd 100644 --- a/storage/iscsi.py +++ b/storage/iscsi.py @@ -88,19 +88,6 @@ def iscsi_get_node_record(node_settings, record): return None -# FIXME replace with libiscsi use -def iscsi_make_node_autostart(disk): - sysfs_path = os.path.realpath("/sys/block/%s/device" %(disk,)) - argv = [ "-m", "session", "-r", sysfs_path ] - log.debug("iscsiadm %s" %(string.join(argv),)) - node_settings = iutil.execWithCapture(ISCSIADM, argv, stderr="/dev/tty5").splitlines() - node_name = iscsi_get_node_record(node_settings, "node.name") - argv = [ "-m", "node", "-T", node_name, "-o", "update", "-n", - "node.startup", "-v", "automatic" ] - log.debug("iscsiadm %s" %(string.join(argv),)) - iutil.execWithRedirect(ISCSIADM, argv, - stdout = "/dev/tty5", stderr="/dev/tty5") - def randomIname(): """Generate a random initiator name the same way as iscsi-iname""" @@ -297,20 +284,21 @@ class iscsi(object): return if not flags.test: - root_drives = [ ] root = anaconda.id.storage.fsset.rootDevice - root_deps = anaconda.id.storage.deviceDeps(root) - for dev in root_deps: - if dev in anaconda.id.storage.disks and \ - dev not in root_drives: - root_drives.append(dev.path) - - log.debug("iscsi.write: root_drives: %s" % (string.join(root_drives),)) - - # set iscsi nodes not used for root to autostart - for disk in anaconda.id.storage.disks: - if isys.driveIsIscsi(disk.path) and not disk in root_drives: - iscsi_make_node_autostart(disk.path) + disks = anaconda.id.storage.devicetree.getDevicesByType("iscsi") + + # set iscsi nodes to autostart + for disk in disks: + # devices used for root get started by the initrd + if root.dependsOn(disk): + continue + # find the iscsi node matching this disk + for node in self.nodes: + if node.name == disk.iscsi_name and \ + node.address == disk.iscsi_address and \ + node.port == disk.iscsi_port: + node.setParameter("node.startup", "automatic"); + break if not os.path.isdir(instPath + "/etc/iscsi"): os.makedirs(instPath + "/etc/iscsi", 0755) diff --git a/storage/udev.py b/storage/udev.py index 1263823ab..a6d3190cb 100644 --- a/storage/udev.py +++ b/storage/udev.py @@ -304,3 +304,33 @@ def udev_device_is_dmraid_partition(info, devicetree): return True return False + +# iscsi disks have ID_PATH in the form of: +# ip-${iscsi_address}:${iscsi_port}-iscsi-${iscsi_tgtname}-lun-${lun} +def udev_device_is_iscsi(info): + try: + path_components = info["ID_PATH"].split("-") + + if info["ID_BUS"] == "scsi" and len(path_components) >= 6 and \ + path_components[0] == "ip" and path_components[2] == "iscsi": + return True + except KeyError: + pass + + return False + +def udev_device_get_iscsi_name(info): + path_components = info["ID_PATH"].split("-") + + # Tricky, the name itself contains atleast 1 - char + return "-".join(path_components[3:len(path_components)-2]) + +def udev_device_get_iscsi_address(info): + path_components = info["ID_PATH"].split("-") + + return path_components[1].split(":")[0] + +def udev_device_get_iscsi_port(info): + path_components = info["ID_PATH"].split("-") + + return path_components[1].split(":")[1] diff --git a/yuminstall.py b/yuminstall.py index eff50ba61..d842a1771 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -1287,13 +1287,6 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon # this takes care of device and filesystem packages map(self.selectPackage, device.packages) - for disk in storage.disks: - # FIXME: this should get handled by the above - if isys.driveIsIscsi(disk.path): - log.info("ensuring iscsi is installed") - self.selectPackage("iscsi-initiator-utils") - break - # anaconda requires several programs on the installed system to complete # installation, but we have no guarantees that some of these will be # installed (they could have been removed in kickstart). So we'll force -- cgit From 164e80fd66563a410579df3d1a521e7557621a71 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 17 Mar 2009 19:06:57 +0100 Subject: Tell NM not to touch interfaces when / is on a network disk This patch renames storage.NetworkDevice to storage.NetworkStorageDevice, as NetworkDevice as classname also is used in network.py . It also changes NetworkStorageDevice so that it does not inherit from Device, as it is now intended for use in multiple inheritance together with StorageDevice (or a derived class) as explained further in the comments in the code. Then this patch changes iScsiDiskDevice and NFSDevice to additional inherit from NetworkStorageDevice (next to what they were already inheriting from. And last if fixes the code in network.py to properly write NM_CONTROLLED=NO when / uses a network backed device. --- network.py | 13 ++++++++----- storage/devices.py | 37 ++++++++++++++++++++----------------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/network.py b/network.py index 4a2eca9d5..5dc9060f0 100644 --- a/network.py +++ b/network.py @@ -581,14 +581,17 @@ class Network: f.write("MTU=%s\n" % dev.get('MTU')) # tell NetworkManager not to touch any interfaces used during - # installation when / is on a network device. Ideally we would only - # tell NM not to touch the interface(s) actually used for /, but we - # have no logic to determine that + # installation when / is on a network backed device. if anaconda is not None: import storage rootdev = anaconda.id.storage.fsset.rootDevice - if isinstance(rootdev, storage.devices.NetworkDevice): - f.write("NM_CONTROLLED=no\n") + # FIXME: use device.host_address to only add "NM_CONTROLLED=no" + # for interfaces actually used enroute to the device + for d in anaconda.id.storage.devices: + if rootdev.dependsOn(d) and isinstance(d, + storage.devices.NetworkStorageDevice): + f.write("NM_CONTROLLED=no\n") + break f.close() os.chmod(newifcfg, 0644) diff --git a/storage/devices.py b/storage/devices.py index c9d63fc42..c74233763 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -405,25 +405,26 @@ class Device(object): return True -class NetworkDevice(Device): - """ A network device """ - _type = "network device" +class NetworkStorageDevice(object): + """ Virtual base class for network backed storage devices """ - def __init__(self, name, parents=None): - """ Create a NetworkDevice instance. + def __init__(self, host_address): + """ Create a NetworkStorage Device instance. Note this class is only + to be used as a baseclass and then only with multiple inheritance. + The only correct use is: + class MyStorageDevice(StorageDevice, NetworkStorageDevice): - Arguments: - - name -- the device name (generally an interface name) - - Keyword Arguments: + The sole purpose of this class is to: + 1) Be able to check if a StorageDevice is network backed + (using isinstance). + 2) To be able to get the host address of the host (server) backing + the storage. - parents -- a list of required Device instances - description -- a string describing the device + Arguments: + host_address -- host address of the backing server """ - Device.__init__(self, name, parents=parents) - self.active = False + self.host_address = host_address class StorageDevice(Device): @@ -2656,7 +2657,7 @@ class DirectoryDevice(FileDevice): self.exists = False -class iScsiDiskDevice(DiskDevice): +class iScsiDiskDevice(DiskDevice, NetworkStorageDevice): """ An iSCSI disk. """ _type = "iscsi" _packages = ["iscsi-initiator-utils"] @@ -2666,6 +2667,7 @@ class iScsiDiskDevice(DiskDevice): self.iscsi_address = kwargs.pop("iscsi_address") self.iscsi_port = int(kwargs.pop("iscsi_port")) DiskDevice.__init__(self, device, **kwargs) + NetworkStorageDevice.__init__(self, self.iscsi_address) log.debug("created new iscsi disk %s %s:%d" % (self.iscsi_name, self.iscsi_address, self.iscsi_port)) class OpticalDevice(StorageDevice): @@ -2803,13 +2805,14 @@ class PRePBootDevice(PartitionDevice): parents=parents, primary=primary) -class NFSDevice(StorageDevice): +class NFSDevice(StorageDevice, NetworkStorageDevice): """ An NFS device """ _type = "nfs" def __init__(self, device, format=None, parents=None): # we could make host/ip, path, &c but will anything use it? - StorageDevice.__init__(device, format=format, parents=parents) + StorageDevice.__init__(self, device, format=format, parents=parents) + NetworkStorageDevice.__init__(self, device.split(":")[0]) @property def path(self): -- cgit From b2b53f1ef3d0fe7526ffe723201153f421c7360a Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 17 Mar 2009 13:49:47 -0400 Subject: Look at CPU flags instead of /proc/iomem to determine PAE-ness (#484941). --- isys/isys.py | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/isys/isys.py b/isys/isys.py index ee1866686..d2168dbf1 100755 --- a/isys/isys.py +++ b/isys/isys.py @@ -655,22 +655,14 @@ def isPaeAvailable(): if not iutil.isX86(): return isPAE - try: - f = open("/proc/iomem", "r") - lines = f.readlines() - for line in lines: - if line[0].isspace(): - continue - start = line.split(' ')[0].split('-')[0] - start = long(start, 16) - - if start >= 0x100000000L: - isPAE = True - break + f = open("/proc/cpuinfo", "r") + lines = f.readlines() + f.close() - f.close() - except: - pass + for line in lines: + if line.startswith("flags") and line.find("pae") != -1: + isPAE = True + break return isPAE -- cgit From 275591c48084283d1e154ebf1fd3b0c941723f9f Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 17 Mar 2009 13:55:17 -0400 Subject: Simplify kernel package selection. --- yuminstall.py | 64 ++++++++++++++++++++--------------------------------------- 1 file changed, 22 insertions(+), 42 deletions(-) diff --git a/yuminstall.py b/yuminstall.py index d842a1771..a5de70225 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -1229,58 +1229,38 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon return None return pkgs[0] - foundkernel = False - kpkg = getBestKernelByArch("kernel", self.ayum) - - # FIXME: this is a bit of a hack. we shouldn't hard-code and - # instead check by provides. but alas. - for k in ("kernel", "kernel-smp", "kernel-PAE"): - if len(self.ayum.tsInfo.matchNaevr(name=k)) > 0: - self.selectModulePackages(anaconda, k) - foundkernel = True - - if not foundkernel and (isys.smpAvailable() or isys.htavailable()): + def selectKernel(pkgname): try: - ksmp = getBestKernelByArch("kernel-smp", self.ayum) + pkg = getBestKernelByArch(pkgname, self.ayum) except PackageSackError: - ksmp = None - log.debug("no kernel-smp package") + log.debug("no %s package" % pkgname) + return False - if ksmp and ksmp.returnSimple("arch") == kpkg.returnSimple("arch"): - foundkernel = True - log.info("selected kernel-smp package for kernel") - self.ayum.install(po=ksmp) - self.selectModulePackages(anaconda, ksmp.name) + if not pkg: + return False - if len(self.ayum.tsInfo.matchNaevr(name="gcc")) > 0: - log.debug("selecting kernel-smp-devel") - self.selectPackage("kernel-smp-devel.%s" % (kpkg.arch,)) + log.info("selected %s package for kernel" % pkg.name) + self.ayum.install(po=pkg) + self.selectModulePackages(anaconda, pkg.name) - if not foundkernel and isys.isPaeAvailable(): - try: - kpae = getBestKernelByArch("kernel-PAE", self.ayum) - except PackageSackError: - kpae = None - log.debug("no kernel-PAE package") + if len(self.ayum.tsInfo.matchNaevr(name="gcc")) > 0: + log.debug("selecting %s-devel" % pkg.name) + self.selectPackage("%s-devel.%s" % (pkg.name, pkg.arch)) + + return True + + foundkernel = False - if kpae and kpae.returnSimple("arch") == kpkg.returnSimple("arch"): + if isys.smpAvailable() or isys.htavailable(): + if selectKernel("kernel-smp"): foundkernel = True - log.info("select kernel-PAE package for kernel") - self.ayum.install(po=kpae) - self.selectModulePackages(anaconda, kpae.name) - if len(self.ayum.tsInfo.matchNaevr(name="gcc")) > 0: - log.debug("selecting kernel-PAE-devel") - self.selectPackage("kernel-PAE-devel.%s" % (kpkg.arch,)) + if not foundkernel and isys.isPaeAvailable(): + if selectKernel("kernel-PAE"): + foundkernel = True if not foundkernel: - log.info("selected kernel package for kernel") - self.ayum.install(po=kpkg) - self.selectModulePackages(anaconda, kpkg.name) - - if len(self.ayum.tsInfo.matchNaevr(name="gcc")) > 0: - log.debug("selecting kernel-devel") - self.selectPackage("kernel-devel.%s" % (kpkg.arch,)) + selectKernel("kernel") def selectFSPackages(self, storage): for device in storage.fsset.devices: -- cgit From 345da3fc16083217d021460be46662c138469569 Mon Sep 17 00:00:00 2001 From: Peter Jones Date: Tue, 17 Mar 2009 14:53:04 -0400 Subject: Fix ppoll() timeout=infinity usage in auditd (#484721). {-1,-1} isn't the right way to send infinity to ppoll(), NULL is. --- isys/auditd.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/isys/auditd.c b/isys/auditd.c index bbc626711..8eef4f393 100644 --- a/isys/auditd.c +++ b/isys/auditd.c @@ -48,7 +48,7 @@ static void do_auditd(int fd) { sigset_t sigs; struct sigaction sa; struct pollfd pds = { - .events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLMSG, + .events = POLLIN, .revents = 0, .fd = fd, }; @@ -71,13 +71,12 @@ static void do_auditd(int fd) { sigdelset(&sigs, SIGHUP); while (1) { - struct timespec timeout = { -1, -1 }; int retval; memset(&rep, 0, sizeof(rep)); do { - retval = ppoll(&pds, 1, &timeout, &sigs); + retval = ppoll(&pds, 1, NULL, &sigs); } while (retval == -1 && errno == EINTR && !done); if (done) -- cgit From b156a5b884ea1decbfaf3e62343896bcca2b51b3 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 17 Mar 2009 15:37:58 -0400 Subject: FormatArgs -> FormatOptions (#490737). --- storage/formats/fs.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index fa931369b..ac2d9b518 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -221,7 +221,7 @@ class FS(DeviceFormat): size = self._size return float(size) - def _getFormatArgs(self, options=None): + def _getFormatOptions(self, options=None): argv = [] if options and isinstance(options, list): argv.extend(options) @@ -263,7 +263,7 @@ class FS(DeviceFormat): if not os.path.exists(self.device): raise FormatCreateError("device does not exist", self.device) - argv = self._getFormatArgs(options=options) + argv = self._getFormatOptions(options=options) w = None if intf: @@ -822,11 +822,11 @@ class BTRFS(FS): _packages = ["btrfs-progs"] _maxSize = 16 * 1024 * 1024 - def _getFormatArgs(self, options=None): + def _getFormatOptions(self, options=None): argv = [] if options and isinstance(options, list): argv.extend(options) - argv.extend(self.defaultFormatArgs) + argv.extend(self.defaultFormatOptions) if self.fslabel: argv.extend(["-L", self.fslabel]) argv.append(self.device) -- cgit From 7f0e17e3b750afb9b74fc48a6e6f786cc0005288 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 17 Mar 2009 15:49:14 -0400 Subject: Fix a traceback when looking for PS3 boot partitions (#490738). --- platform.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/platform.py b/platform.py index a32909400..6ce5c7746 100644 --- a/platform.py +++ b/platform.py @@ -277,10 +277,10 @@ class NewWorldPPC(PPC): def bootDevice(self): bootDev = None - for device in self.anaconda.id.storage.devices.values(): + for part in self.anaconda.id.storage.partitions: # XXX do we need to also check the size? - if device.format.type == "hfs" and device.bootable: - bootDev = device + if part.format.type == "hfs" and part.bootable: + bootDev = part return bootDev -- cgit From 4b0c72ccf680499df5b91723df5645b5e4275104 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 17 Mar 2009 16:17:22 -0400 Subject: Make platform.checkBootRequest work better and not use diskset anymore. --- platform.py | 61 ++++++++++++++++++------------------------------------------- 1 file changed, 18 insertions(+), 43 deletions(-) diff --git a/platform.py b/platform.py index 6ce5c7746..71db883a8 100644 --- a/platform.py +++ b/platform.py @@ -97,7 +97,7 @@ class Platform(object): def bootloaderPackage(self): return self._bootloaderPackage - def checkBootRequest(self, req, diskset): + def checkBootRequest(self, req): """Perform an architecture-specific check on the boot device. Not all platforms may need to do any checks. Raises an exception if there is a problem, or returns True otherwise.""" @@ -144,25 +144,13 @@ class EFI(Platform): ret["boot"] = (bootDev.name, N_("EFI System Partition")) return ret - def checkBootRequest(self, req, diskset): - if not req.device or not hasattr(req, "drive"): - return - - bootPart = None - for drive in req.drive: - bootPart = diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device) - if bootPart: - break - - if not bootPart: - return - - if req.mountpoint == "/boot": - if not bootPart.fileSystem.type.startswith("ext"): + def checkBootRequest(self, req): + if req.format.mountpoint == "/boot": + if not req.format.type.startswith("ext"): raise FSError("/boot is not ext2") - elif req.mountpoint == "/boot/efi": - if not bootPart.fileSystem.type.startswith("fat"): - raise FSError("/boot/efi is not vfat") + elif req.format.mountpoint == "/boot/efi": + if not req.format.type.endswith("fat"): + raise FSError("/boot/efi is not vfat") def setDefaultPartitioning(self): ret = Platform.setDefaultPartitioning(self) @@ -178,20 +166,12 @@ class EFI(Platform): class Alpha(Platform): _diskType = parted.diskType["bsd"] - def checkBootRequest(self, req, diskset): - if not req.device or not hasattr(req, "drive"): - return - - bootPart = None - for drive in req.drive: - bootPart = diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device) - if bootPart: - break - - if not bootPart: - return + def checkBootRequest(self, req): + disk = req.disk + if not disk: + raise DeviceError("Boot partition has no disk") - disk = bootPart.disk + disk = disk.partedDisk # Check that we're a BSD disk label if not disk.type == self.diskType: @@ -253,18 +233,13 @@ class IPSeriesPPC(PPC): return ret - def checkBootRequest(self, req, diskset): - if not req.device or not hasattr(req, "drive"): - return - - bootPart = None - for drive in req.drive: - bootPart = diskset.disks[drive].getPartitionByPath("/dev/%s" % req.device) - if bootPart and (bootPart.geometry.end * bootPart.geometry.device.sectorSize / - (1024.0 * 1024)) > 4096: - raise DeviceError("Boot partition is located too high") + def checkBootRequest(self, req): + bootPart = getattr(req, "partedPartition", None) + if not bootPart: + raise DeviceError("Boot partition has no partedPartition") - return + if bootPart.geometry.end * bootPart.geometry.device.sectorSize / (1024.0 * 1024) > 4096: + raise DeviceError("Boot partition is located too high") def setDefaultPartitioning(self): ret = PPC.setDefaultPartitioning(self) -- cgit From 477b63db3dc4c6a75896fffb27adf7d4e821d6e5 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 17 Mar 2009 16:20:48 -0400 Subject: Typo fix. --- storage/devices.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index c74233763..93d3c118c 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -748,10 +748,10 @@ class DiskDevice(StorageDevice): except _ped.DeviceException: pass - log.debug("creating parted Disk: %s" % self.path) if self.partedDevice: + log.debug("creating parted Disk: %s" % self.path) if initlabel: - self.partedDisk = self.fresPartedDisk() + self.partedDisk = self.freshPartedDisk() else: try: self.partedDisk = parted.Disk(device=self.partedDevice) -- cgit From a0493f6d329eb4294c6466b895bb835a6c8bc74a Mon Sep 17 00:00:00 2001 From: Jesse Keating Date: Tue, 17 Mar 2009 23:55:52 -0700 Subject: New release --- anaconda.spec | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index a376a9b68..48e1564cb 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.31 +Version: 11.5.0.32 Release: 1 License: GPLv2+ Group: Applications/System @@ -210,6 +210,20 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Tue Mar 17 2009 Jesse Keating - 11.5.0.32-1 +- Typo fix. (clumens) +- Make platform.checkBootRequest work better and not use diskset anymore. (clumens) +- Fix a traceback when looking for PS3 boot partitions (#490738). (clumens) +- FormatArgs -> FormatOptions (#490737). (clumens) +- Fix ppoll() timeout=infinity usage in auditd (#484721). (pjones) +- Simplify kernel package selection. (clumens) +- Look at CPU flags instead of /proc/iomem to determine PAE-ness (#484941). (clumens) +- Tell NM not to touch interfaces when / is on a network disk (hdegoede) +- Get iscsi going with the new storage code (hdegoede) +- Use minihal instead of isys.hardDriveDict in list-harddrives (#488122). (clumens) +- storage.disks never includes disks without media present. (clumens) +- Changed the getDevicebyLabel() to getDeviceByLabel() in devicetree.py (mgracik) + * Mon Mar 16 2009 David Cantrell - 11.5.0.31-1 - Don't use disk.maximizePartition anymore. (dlehman) - Only schedule implicit format destruction if there is formatting to -- cgit From 2fd77ba8e2b390ea6de7256bff37451b5f868efb Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 17 Mar 2009 19:28:44 +0100 Subject: We no longer use iscsiadm anywhere Hurray! --- storage/iscsi.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/storage/iscsi.py b/storage/iscsi.py index 1add6eebd..11d36562b 100644 --- a/storage/iscsi.py +++ b/storage/iscsi.py @@ -46,8 +46,6 @@ except: # Note that stage2 copies all files under /sbin to /usr/sbin global ISCSID ISCSID="" -global ISCSIADM -ISCSIADM = "" INITIATOR_FILE="/etc/iscsi/initiatorname.iscsi" def find_iscsi_files(): @@ -57,20 +55,13 @@ def find_iscsi_files(): path="%s/iscsid" % (dir,) if os.access(path, os.X_OK): ISCSID=path - global ISCSIADM - if ISCSIADM == "": - for dir in ("/usr/sbin", "/tmp/updates", "/mnt/source/RHupdates"): - path="%s/iscsiadm" % (dir,) - if os.access(path, os.X_OK): - ISCSIADM=path def has_iscsi(): find_iscsi_files() - if ISCSID == "" or ISCSIADM == "" or not has_libiscsi: + if ISCSID == "" or not has_libiscsi: return False log.info("ISCSID is %s" % (ISCSID,)) - log.info("ISCSIADM is %s" % (ISCSIADM,)) # make sure the module is loaded if not os.access("/sys/module/iscsi_tcp", os.X_OK): -- cgit From c3bbce8068a6c994e38718819d55bcc09d3f56fc Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 17 Mar 2009 20:10:44 +0100 Subject: Use getDevicesByInstance() for storage.partitions --- storage/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 08ba90aed..b0789d6d9 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -262,11 +262,7 @@ class Storage(object): does not necessarily reflect the actual on-disk state of the system's disks. """ - partitions = [] - devices = self.devicetree.devices - for d in devices: - if isinstance(devices[d], PartitionDevice): - partitions.append(devices[d]) + partitions = self.devicetree.getDevicesByInstance(PartitionDevice) partitions.sort(key=lambda d: d.name) return partitions -- cgit From a3d472c9bc45f8eae4924e7adc89aa90eae01922 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 17 Mar 2009 20:44:58 +0100 Subject: Fix result of updateSysfsPath to be consistent with initial sysfsPath values Initial sysfsPath values as passed into __init__ at Device creation do not start with /sys and are canonalized. This patch fixes the various updateSysfsPath methods to set the sysfsPath attribute to a new value consistent with this. --- storage/devices.py | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 93d3c118c..367e0444e 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -441,7 +441,7 @@ class StorageDevice(Device): """ _type = "storage device" devDir = "/dev" - sysfsBlockDir = "/class/block" + sysfsBlockDir = "class/block" _resizable = False def __init__(self, device, format=None, @@ -521,9 +521,8 @@ class StorageDevice(Device): def updateSysfsPath(self): """ Update this device's sysfs path. """ log_method_call(self, self.name, status=self.status) - self.sysfsPath = os.path.join("/sys", - self.sysfsBlockDir, - self.name) + path = os.path.join("/sys", self.sysfsBlockDir, self.name) + self.sysfsPath = os.path.realpath(path)[4:] log.debug("%s sysfsPath set to %s" % (self.name, self.sysfsPath)) @property @@ -1346,9 +1345,10 @@ class DMDevice(StorageDevice): if self.status: dm_node = self.getDMNode() - self.sysfsPath = os.path.join("/sys", self.sysfsBlockDir, dm_node) + path = os.path.join("/sys", self.sysfsBlockDir, dm_node) + self.sysfsPath = os.path.realpath(path)[4:] else: - self.sysfsPath = None + self.sysfsPath = '' #def getTargetType(self): # return dm.getDmTarget(name=self.name) @@ -2148,11 +2148,9 @@ class MDRaidArrayDevice(StorageDevice): raise DeviceError("device has not been created") if self.status: - self.sysfsPath = os.path.join("/sys", - self.sysfsBlockDir, - self.name) + self.sysfsPath = "/devices/virtual/block/%s" % self.name else: - self.sysfsPath = None + self.sysfsPath = '' def _addDevice(self, device): """ Add a new member device to the array. -- cgit From 904d5c912092436d17f8c6c19dc47882241f6aef Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 17 Mar 2009 22:21:28 +0100 Subject: Set format UUID after creating a format This fixes us having /dev/foo instead of UUID= in fstab for anaconda created filesystems. --- storage/deviceaction.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/storage/deviceaction.py b/storage/deviceaction.py index f9a73229d..3f7ce916d 100644 --- a/storage/deviceaction.py +++ b/storage/deviceaction.py @@ -24,7 +24,7 @@ import copy from parted import PARTITION_BOOT -from udev import udev_settle +from udev import * from devices import StorageDevice, PartitionDevice from formats import getFormat @@ -278,6 +278,11 @@ class ActionCreateFormat(DeviceAction): self.device.format.create(intf=intf, device=self.device.path, options=self.device.formatArgs) + # Get the UUID now that the format is created + udev_settle() + self.device.updateSysfsPath() + info = udev_get_block_device("/sys%s" % self.device.sysfsPath) + self.device.format.uuid = udev_device_get_uuid(info) def cancel(self): self.device.format = self.origFormat -- cgit From c74e6239d8600a0b85638f61729e3886a5d801a4 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 18 Mar 2009 10:26:24 +0100 Subject: Stop overriding __init__ in DMRaidPartitionDevice We were overriding __init__ in DMRaidPartitionDevice only to pass everything through to our supers __init__. Lately we've been doing a bad job at this as we forget to pass through grow and maxSize causing troubles. Since all we want to do is pass everything through just not overriding __init__ to begin with is the by far the best fix. --- storage/devices.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 367e0444e..8aef71162 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -2423,17 +2423,6 @@ class DMRaidPartitionDevice(PartitionDevice): _packages = ["dmraid"] devDir = "/dev/mapper" - def __init__(self, name, format=None, - size=None, grow=False, maxsize=None, - major=None, minor=None, bootable=None, - sysfsPath='', parents=None, exists=None, - partType=None, primary=False): - PartitionDevice.__init__(self, name, format=format, size=size, - major=major, minor=minor, bootable=bootable, - sysfsPath=sysfsPath, parents=parents, - exists=exists, partType=partType, - primary=primary) - # We are more like a partition then a dmdevice, but we are still a dmdevice # so we need to "inherit" some functions from dmdevice def updateSysfsPath(self): -- cgit From 2b05419b71b0380f6b4b9c088ae27f10963ff4fd Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 18 Mar 2009 13:25:06 +0100 Subject: Make PartitionDevice handle both normal and dmraid partitions The way we were handling updateSysfsPath in DMRaidPartitionDevice was very wrong, you cannot call a method of an other class on self, when you do not inherit from that class. Given the minimal difference between normal and dmraid partitions (only path and sysfsPath differ) and all the pain having multiple partition classes causes, this patch makes PartitionDevice handle both normal and dmraid partitions, and gets rid of the whole partition factory thing. --- storage/__init__.py | 2 +- storage/devices.py | 151 +++++++++++++----------------------------------- storage/devicetree.py | 31 +++++----- storage/partitioning.py | 5 +- 4 files changed, 57 insertions(+), 132 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index b0789d6d9..6bb7a847c 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -495,7 +495,7 @@ class Storage(object): else: name = "req%d" % self.nextID - return PartitionDeviceFactory(name, *args, **kwargs) + return PartitionDevice(name, *args, **kwargs) def newMDArray(self, *args, **kwargs): """ Return a new MDRaidArrayDevice instance for configuring. """ diff --git a/storage/devices.py b/storage/devices.py index 8aef71162..0922778c3 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -130,81 +130,6 @@ def get_device_majors(): return majors device_majors = get_device_majors() -def PartitionDeviceFactory(*args, **kwargs): - """This will decide what PartitionDevice class will be used. - - Arguments: - - name -- the device name (generally a device node's basename) - - Keyword Arguments: - - exists -- indicates whether this is an existing device - format -- the device's format (DeviceFormat instance) - - For existing partitions: - - parents -- the disk that contains this partition - major -- the device major - minor -- the device minor - sysfsPath -- sysfs device path - - For new partitions: - - partType -- primary,extended,&c (as parted constant) - grow -- whether or not to grow the partition - maxsize -- max size for growable partitions (in MB) - size -- the device's size (in MB) - bootable -- whether the partition is bootable - parents -- a list of potential containing disks - - The decision will be made base on finding the disk by name - """ - # FIXME: PRePBootDevice should be here somehow.!!! - roots = ["/dev", "/dev/mapper"] - # firs lets normalize the name - norm_name = args[0].split("/")[-1] - - # We look for the disk in /dev. From PartitionDevice its the [0] one. - if not kwargs.get("exists") and not kwargs.get("parents"): - # Cant really choose a good type of class, default to PartitionDevice - # This will be considered as a request. - return PartitionDevice(*args, **kwargs) - elif not kwargs.get("exists"): - # some requests have a disk specified - parents = kwargs["parents"] - if isinstance(parents, Device): - parents = [parents] - - if isinstance(parents[0], DMRaidArrayDevice): - return DMRaidPartitionDevice(*args, **kwargs) - else: - return PartitionDevice(*args, **kwargs) - else: - parents = kwargs["parents"] - if isinstance(parents, Device): - parents = [parents] - # we receive a list of parents. look for the disk that contains the name. - for root in roots: - for parent in parents: - path = os.path.join(root,norm_name) - log.debug("looking up parted Partition: %s" % path) - part = parent.partedDisk.getPartitionByPath(path) - if not part: - continue - else: - # we have found a part. no make the decision based on path - if path.startswith(roots[1]): - #we create a DMPartition - return DMRaidPartitionDevice(*args, **kwargs) - elif path.startswith(roots[0]): - # make sure that the parent is consistent - kwargs["parents"] = [parent] - return PartitionDevice(*args, **kwargs) - - # If we get here, we did not find anything. - log.warning("unable to find parted device for %s" % norm_name) - return None class Device(object): """ A generic device. @@ -440,7 +365,7 @@ class StorageDevice(Device): should create a filesystem if one has been specified. """ _type = "storage device" - devDir = "/dev" + _devDir = "/dev" sysfsBlockDir = "class/block" _resizable = False @@ -512,7 +437,7 @@ class StorageDevice(Device): @property def path(self): """ Device node representing this device. """ - return "%s/%s" % (self.devDir, self.name) + return "%s/%s" % (self._devDir, self.name) def probe(self): """ Probe for any missing information about this device. """ @@ -1022,6 +947,16 @@ class PartitionDevice(StorageDevice): start=geom.start, end=geom.end) self.partedPartition = None + @property + def path(self): + """ Device node representing this device. """ + if not self.parents: + # Bogus, but code in various places compares devices by path + # So we must return something unique + return self.name + + return "%s/%s" % (self.parents[0]._devDir, self.name) + @property def partType(self): """ Get the partition's type (as parted constant). """ @@ -1082,6 +1017,20 @@ class PartitionDevice(StorageDevice): partedPartition = property(lambda d: d._getPartedPartition(), lambda d,p: d._setPartedPartition(p)) + def updateSysfsPath(self): + """ Update this device's sysfs path. """ + log_method_call(self, self.name, status=self.status) + if not self.parents: + self.sysfsPath = '' + + elif self.parents[0]._devDir == "/dev/mapper": + dm_node = dm.dm_node_from_name(self.name) + path = os.path.join("/sys", self.sysfsBlockDir, dm_node) + self.sysfsPath = os.path.realpath(path)[4:] + + else: + StorageDevice.updateSysfsPath(self) + def dependsOn(self, dep): """ Return True if this device depends on dep. """ if isinstance(dep, PartitionDevice) and dep.isExtended and self.isLogical: @@ -1265,16 +1214,6 @@ class PartitionDevice(StorageDevice): self.parents = [disk] disk.addChild() - if isinstance(disk, DMRaidArrayDevice): - # modify the partition so it can look like the DMRaidPartitionDevice. - - self._type = "dmraid partition" - self._packages = ["dmraid"] - self.devDir = "/dev/mapper" - self.updateSysfsPath = DMDevice.updateSysfsPath - self.getDMNode = DMDevice.getDMNode - - disk = property(lambda p: p._getDisk(), lambda p,d: p._setDisk(d)) @@ -1293,7 +1232,7 @@ class PartitionDevice(StorageDevice): class DMDevice(StorageDevice): """ A device-mapper device """ _type = "dm" - devDir = "/dev/mapper" + _devDir = "/dev/mapper" def __init__(self, name, format=None, size=None, dmUuid=None, target=None, exists=None, parents=None, sysfsPath=''): @@ -2326,7 +2265,7 @@ class DMRaidArrayDevice(DiskDevice): """ A dmraid (device-mapper RAID) device """ _type = "dm-raid array" _packages = ["dmraid"] - devDir = "/dev/mapper" + _devDir = "/dev/mapper" def __init__(self, name, raidSet=None, level=None, format=None, size=None, major=None, minor=None, parents=None, @@ -2406,30 +2345,18 @@ class DMRaidArrayDevice(DiskDevice): def deactivate(self): self._raidSet.deactivate() - # We are more like a disk then a dmdevice, but we are still a dmdevice, - # so we need to "inherit" some functions from dmdevice def updateSysfsPath(self): - DMDevice.updateSysfsPath(self) - - def getDMNode(self): - DMDevice.getDMNode(self) - - -class DMRaidPartitionDevice(PartitionDevice): - """ A disk partition in a dmraid array, identical to a general - PartitionDevice, except for the device path and sysfs path - """ - _type = "dmraid partition" - _packages = ["dmraid"] - devDir = "/dev/mapper" - - # We are more like a partition then a dmdevice, but we are still a dmdevice - # so we need to "inherit" some functions from dmdevice - def updateSysfsPath(self): - DMDevice.updateSysfsPath(self) + """ Update this device's sysfs path. """ + log_method_call(self, self.name, status=self.status) + if not self.exists: + raise DeviceError("device has not been created") - def getDMNode(self): - DMDevice.getDMNode(self) + if self.status: + dm_node = dm.dm_node_from_name(self.name) + path = os.path.join("/sys", self.sysfsBlockDir, dm_node) + self.sysfsPath = os.path.realpath(path)[4:] + else: + self.sysfsPath = '' class MultipathDevice(DMDevice): @@ -2518,7 +2445,7 @@ class FileDevice(StorageDevice): This exists because of swap files. """ _type = "file" - devDir = "" + _devDir = "" def __init__(self, path, format=None, size=None, exists=None, parents=None): diff --git a/storage/devicetree.py b/storage/devicetree.py index 3a2af94c3..8ba48647d 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -897,16 +897,11 @@ class DeviceTree(object): udev_device_is_dmraid_partition(info, self): diskname = udev_device_get_dmraid_partition_disk(info) disk = self.getDeviceByName(diskname) - device = PartitionDeviceFactory(name, \ - sysfsPath=sysfs_path, \ - major=udev_device_get_major(info), \ - minor=udev_device_get_minor(info), \ - exists=True, \ - parents=[disk]) - if not device: - return + device = PartitionDevice(name, sysfsPath=sysfs_path, + major=udev_device_get_major(info), + minor=udev_device_get_minor(info), + exists=True, parents=[disk]) self._addDevice(device) - #self.addIgnoredDisk(name) # if we get here, we found all of the slave devices and # something must be wrong -- if all of the slaves are in @@ -1058,14 +1053,18 @@ class DeviceTree(object): log.error("failure scanning device %s" % disk_name) return - device = PartitionDeviceFactory(name, - sysfsPath=sysfs_path, - major=udev_device_get_major(info), - minor=udev_device_get_minor(info), - exists=True, - parents=[disk]) - if not device: + try: + device = PartitionDevice(name, sysfsPath=sysfs_path, + major=udev_device_get_major(info), + minor=udev_device_get_minor(info), + exists=True, parents=[disk]) + except DeviceError: + # corner case sometime the kernel accepts a partition table + # which gets rejected by parted, in this case we will + # prompt to re-initialize the disk, so simply skip the + # faulty partitions. return + self._addDevice(device) # diff --git a/storage/partitioning.py b/storage/partitioning.py index 8243080de..92458d94f 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -32,7 +32,7 @@ from constants import * from errors import * from deviceaction import * -from devices import PartitionDeviceFactory, LUKSDevice +from devices import PartitionDevice, LUKSDevice import gettext _ = lambda x: gettext.ldgettext("anaconda", x) @@ -568,8 +568,7 @@ def doPartitioning(storage, exclusiveDisks=None): # that does not exist means leaving self.parents empty and instead # populating self.req_disks. In this case, we need to skip past # that since this partition is already defined. - device = PartitionDeviceFactory(extended.getDeviceNodeName(), - parents=disk) + device = PartitionDevice(extended.getDeviceNodeName(), parents=disk) device.parents = [disk] device.partedPartition = extended storage.createDevice(device) -- cgit From 4f797e93d54bded4130336885addd4f04ab39d3d Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Mar 2009 11:16:14 -0400 Subject: Don't try to fit the whole StorageDevice.__str__ output into the UI (#490406). --- textw/partition_text.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textw/partition_text.py b/textw/partition_text.py index 41e1db5cc..d74c7d491 100644 --- a/textw/partition_text.py +++ b/textw/partition_text.py @@ -106,7 +106,7 @@ class PartitionTypeWindow: selected = 0 sizestr = "%8.0f MB" % (disk.size,) - diskdesc = "%6s %s (%s)" % (disk, sizestr, model[:24],) + diskdesc = "%6s %s (%s)" % (disk.path, sizestr, model[:24],) drivelist.append(diskdesc, selected = selected) -- cgit From d7df78d3a9ea0af6d37ebd1c3f3a7c99dceb5227 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Tue, 17 Mar 2009 19:13:00 +0100 Subject: Offer available partitions when editing non-preexisting raid request. --- iw/raid_dialog_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index cab2bb067..6cd4f2f91 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -63,7 +63,7 @@ class RaidEditor: partname = "%s" % part.name partsize = "%8.0f MB" % part.size - if self.isNew: + if not self.origrequest.exists: partlist.append_row((partname, partsize), False) else: if part in tempDevList: -- cgit From db364a06430fbd3c4f82f339c6bc5d837b986638 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Tue, 17 Mar 2009 19:13:44 +0100 Subject: Do not duplicate requested minor number in edit raid UI list. --- iw/raid_dialog_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index 6cd4f2f91..976edc965 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -393,7 +393,7 @@ class RaidEditor: if not origrequest.exists: availminors = self.storage.unusedMDMinors[:16] reqminor = origrequest.minor - if reqminor is not None: + if reqminor is not None and reqminor not in availminors: availminors.append(reqminor) availminors.sort() -- cgit From 7d82ba48314aa8be4b161a30334366e6f58bd921 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Tue, 17 Mar 2009 19:33:24 +0100 Subject: Fix removing of devices with the same name from tree when adding create action. Bug met when reediting new raid array in UI. When we register create device action, we want to remove (beside the created device) also devices with the same name from the tree, which needs the fix. --- storage/devicetree.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index 8ba48647d..850a22039 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -684,13 +684,15 @@ class DeviceTree(object): (action.isCreate() and action.isFormat())) and \ action.device not in self._devices: raise DeviceTreeError("device is not in the tree") - elif (action.isCreate() and action.isDevice()) and \ - (action.device in self._devices or \ - action.device.path in [d.path for d in self._devices]): + elif (action.isCreate() and action.isDevice()): # this allows multiple create actions w/o destroy in between; # we will clean it up before processing actions #raise DeviceTreeError("device is already in the tree") - self._removeDevice(action.device) + if action.device in self._devices: + self._removeDevice(action.device) + for d in self._devices: + if d.path == action.device.path: + self._removeDevice(d) if action.isCreate() and action.isDevice(): self._addDevice(action.device) -- cgit From 6ec88a43e537960c2759963a3342810d8b1db6c0 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Wed, 18 Mar 2009 11:48:26 +0100 Subject: Fix getChildren call in partition UI --- iw/partition_gui.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index 668530c81..0b52a70fb 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -737,7 +737,7 @@ class PartitionWindow(InstallWindow): # look up the mapped/decrypted device since that's # where we'll find the format we want to display try: - dm_dev = self.storage.getChildren(array)[0] + dm_dev = self.storage.devicetree.getChildren(array)[0] except IndexError: format = array.format else: -- cgit From 49d79e014b352055ce51b0322392114cc01638c9 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Wed, 18 Mar 2009 11:50:33 +0100 Subject: actionDestroyFormat call takes device, not format --- iw/raid_dialog_gui.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index 976edc965..e4fbf1363 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -192,7 +192,7 @@ class RaidEditor: except IndexError: pass else: - actions.append(ActionDestroyFormat(luksdev.format)) + actions.append(ActionDestroyFormat(luksdev)) actions.append(ActionDestroyDevice(luksdev)) luksdev = None @@ -222,7 +222,7 @@ class RaidEditor: except IndexError: pass else: - actions.append(ActionDestroyFormat(luksdev.format)) + actions.append(ActionDestroyFormat(luksdev)) actions.append(ActionDestroyDevice(luksdev)) luksdev = None -- cgit From 73f693a89be68ec4bca8408d1150c575b08cd7ce Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Wed, 18 Mar 2009 13:39:27 +0100 Subject: Editing non-existent raid device by destroying and creating actions --- iw/raid_dialog_gui.py | 79 ++++++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index e4fbf1363..5daab4169 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -158,11 +158,11 @@ class RaidEditor: raidmembers.append(dev) iter = model.iter_next(iter) + mountpoint = self.mountCombo.get_children()[0].get_text() if not self.origrequest.exists: # new device fmt_class = self.fstypeCombo.get_active_value() - mountpoint = self.mountCombo.get_children()[0].get_text() raidminor = int(self.minorCombo.get_active_value()) model = self.levelcombo.get_model() @@ -175,17 +175,26 @@ class RaidEditor: spares = 0 format = fmt_class(mountpoint=mountpoint) - if self.fsoptionsDict.has_key("lukscb") and \ - self.fsoptionsDict["lukscb"].get_active() and \ - self.origrequest.format.type != "luks": - luksdev = LUKSDevice("luks-%s" % self.origrequest.name, + members = len(raidmembers) - spares + level = int(raidlevel.lower().replace("raid", "")) + + request = self.storage.newMDArray(minor=raidminor, + level=level, + format=format, + parents=raidmembers, + totalDevices=len(raidmembers), + memberDevices=members) + + if self.lukscb and self.lukscb.get_active() and \ + (self.isNew or self.origrequest.format.type != "luks"): + luksdev = LUKSDevice("luks-%s" % request.name, format=format, - parents=self.origrequest) + parents=request) format = getFormat("luks", passphrase=self.storage.encryptionPassphrase) - elif self.fsoptionsDict.has_key("lukscb") and \ - not self.fsoptionsDict["lukscb"].get_active() and \ - self.origrequest.format.type == "luks": + request.format = format + elif self.lukscb and not self.lukscb.get_active() and \ + not self.isNew and self.origrequest.format.type == "luks": # destroy luks format and mapped device try: luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] @@ -196,11 +205,20 @@ class RaidEditor: actions.append(ActionDestroyDevice(luksdev)) luksdev = None + # XXXRV not needed as we destroy origrequest ? actions.append(ActionDestroyFormat(self.origrequest)) + + if not self.isNew: + # This may be handled in devicetree.registerAction, + # but not in case when we change minor and thus + # device name/path (at least with current md) + actions.append(ActionDestroyDevice(self.origrequest)) + actions.append(ActionCreateDevice(request)) + actions.append(ActionCreateFormat(request)) + else: # existing device fmt_class = self.fsoptionsDict["fstypeCombo"].get_active_value() - mountpoint = self.mountCombo.get_children()[0].get_text() if self.fsoptionsDict.has_key("formatcb") and \ self.fsoptionsDict["formatcb"].get_active(): format = fmt_class(mountpoint=mountpoint) @@ -241,28 +259,17 @@ class RaidEditor: usedev = origrequest migrate = True - if self.origrequest.format.exists and \ - self.storage.formatByDefault(self.origrequest): - if not queryNoFormatPreExisting(self.intf): - continue + if self.origrequest.format.exists and \ + self.storage.formatByDefault(self.origrequest): + if not queryNoFormatPreExisting(self.intf): + continue + + if format: + actions.append(ActionCreateFormat(self.origrequest, format)) # everything ok, break out break - if not self.origrequest.exists: - members = len(raidmembers) - spares - level = int(raidlevel.lower().replace("raid", "")) - request = self.storage.newMDArray(minor=raidminor, - level=level, - format=format, - parents=raidmembers, - totalDevices=len(raidmembers), - memberDevices=members) - actions.append(ActionCreateDevice(request)) - actions.append(ActionCreateFormat(request)) - elif format: - actions.append(ActionCreateFormat(self.origrequest, format)) - if luksdev: actions.append(ActionCreateDevice(luksdev)) @@ -329,14 +336,6 @@ class RaidEditor: maintable.set_col_spacings(5) row = 0 - # Mount Point entry - lbl = createAlignedLabel(_("_Mount Point:")) - maintable.attach(lbl, 0, 1, row, row + 1) - self.mountCombo = createMountPointCombo(origrequest) - lbl.set_mnemonic_widget(self.mountCombo) - maintable.attach(self.mountCombo, 1, 2, row, row + 1) - row = row + 1 - # we'll maybe add this further down self.lukscb = gtk.CheckButton(_("_Encrypt")) self.lukscb.set_data("formatstate", 1) @@ -356,6 +355,14 @@ class RaidEditor: usedev = origrequest format = origrequest.format + # Mount Point entry + lbl = createAlignedLabel(_("_Mount Point:")) + maintable.attach(lbl, 0, 1, row, row + 1) + self.mountCombo = createMountPointCombo(usedev) + lbl.set_mnemonic_widget(self.mountCombo) + maintable.attach(self.mountCombo, 1, 2, row, row + 1) + row = row + 1 + # Filesystem Type if not origrequest.exists: lbl = createAlignedLabel(_("_File System Type:")) -- cgit From 177e0df8569d8adc7ec8921edf18127d63d89ef0 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Wed, 18 Mar 2009 14:05:12 +0100 Subject: Destroy and create luks child of raid array too when editing in UI. --- iw/raid_dialog_gui.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index 5daab4169..578e3e7b3 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -185,17 +185,11 @@ class RaidEditor: totalDevices=len(raidmembers), memberDevices=members) - if self.lukscb and self.lukscb.get_active() and \ - (self.isNew or self.origrequest.format.type != "luks"): - luksdev = LUKSDevice("luks-%s" % request.name, - format=format, - parents=request) - format = getFormat("luks", - passphrase=self.storage.encryptionPassphrase) - request.format = format - elif self.lukscb and not self.lukscb.get_active() and \ - not self.isNew and self.origrequest.format.type == "luks": + # we must destroy luks leaf before original raid request + if self.origrequest.format.type == "luks": + # => not self.isNew # destroy luks format and mapped device + # XXX remove catching, it should always succeed try: luksdev = self.storage.devicetree.getChildren(self.origrequest)[0] except IndexError: @@ -205,6 +199,16 @@ class RaidEditor: actions.append(ActionDestroyDevice(luksdev)) luksdev = None + if self.lukscb and self.lukscb.get_active(): + luksdev = LUKSDevice("luks-%s" % request.name, + format=format, + parents=request) + format = getFormat("luks", + passphrase=self.storage.encryptionPassphrase) + request.format = format + elif self.lukscb and not self.lukscb.get_active() and \ + self.origrequest.format.type == "luks": + # XXXRV not needed as we destroy origrequest ? actions.append(ActionDestroyFormat(self.origrequest)) -- cgit From be146f32675e09e5a2f40a48790f9ad9600fffb8 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Wed, 18 Mar 2009 16:27:08 +0100 Subject: Do not hang when creating raid array with member having filesystem detected (#490891) --- storage/devicelibs/mdraid.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devicelibs/mdraid.py b/storage/devicelibs/mdraid.py index a1a40d2e1..d02b6eae1 100644 --- a/storage/devicelibs/mdraid.py +++ b/storage/devicelibs/mdraid.py @@ -101,7 +101,7 @@ def get_raid_max_spares(raidlevel, nummembers): raise ValueError, "invalid raid level %d" % raidlevel def mdcreate(device, level, disks, spares=0): - argv = ["--create", device, "--level", str(level)] + argv = ["--create", device, "--run", "--level", str(level)] raid_devs = len(disks) - spares argv.append("--raid-devices=%d" % raid_devs) if spares: -- cgit From 9b0ba331294a92adb34b8fd5113f0da627225af7 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 18 Mar 2009 14:44:16 +0100 Subject: If a mountpoint depends on a network disk at _netdev to its fstab options --- storage/__init__.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/storage/__init__.py b/storage/__init__.py index 6bb7a847c..58f7fbf4f 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1603,6 +1603,7 @@ class FSSet(object): devices = self.mountpoints.values() + self.swapDevices devices.extend([self.devshm, self.devpts, self.sysfs, self.proc]) + netdevs = self.devicetree.getDevicesByInstance(NetworkStorageDevice) for device in devices: # why the hell do we put swap in the fstab, anyway? if not device.format.mountable and device.format.type != "swap": @@ -1622,6 +1623,13 @@ class FSSet(object): continue options = options or "defaults" + for netdev in netdevs: + if device.dependsOn(netdev): + if mountpoint == "/": + options = options + ",_rnetdev" + else: + options = options + ",_netdev" + break devspec = device.fstabSpec dump = device.format.dump if device.format.check and mountpoint == "/": -- cgit From d380c8d630d40ea6aaf513e3f0941f44b0334394 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 18 Mar 2009 16:26:59 +0100 Subject: Do not write LV uuid to grub.conf, but the filesystem uuid When re-using an existing LV for /, and thus one which has uuid set in its representing Device, we would write the LV uuid to grub.conf as root= parameter, resulting in a non booting system. Also we no longer keep labels anywhere, so don't look for a label. Note that we are only hitting this with pre-existing LV's because we are not setting / updating the LVDevice's uuid when creating a new one (something which we ought to fix). --- booty/bootloaderInfo.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 0c1ec5b5b..90073e279 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -89,14 +89,12 @@ def getRootDevName(initrd, rootDevice): return rootDevice.path try: - if rootDevice.uuid: - return "UUID=%s" % rootDevice.uuid - elif rootDevice.label: - return "LABEL=%s" % rootDevice.label - - return rootDevice.path + if rootDevice.format.uuid: + return "UUID=%s" % rootDevice.format.uuid except: - return rootDevice.path + pass + + return rootDevice.path class KernelArguments: -- cgit From 23c2a553506ffc8a11051c63730926c6c7cc3f41 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 18 Mar 2009 16:29:47 +0100 Subject: Do not traceback when writing anaconda.ks with iscsi with auth info. --- storage/iscsi.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/iscsi.py b/storage/iscsi.py index 11d36562b..7c425174d 100644 --- a/storage/iscsi.py +++ b/storage/iscsi.py @@ -263,11 +263,11 @@ class iscsi(object): f.write("iscsi --ipaddr %s --port %s" %(n.address, n.port)) auth = n.getAuth() if auth: - f.write(" --user %s" %(n.username,)) - f.write(" --password %s" %(n.password,)) + f.write(" --user %s" % auth.username) + f.write(" --password %s" % auth.password) if len(auth.reverse_username): - f.write(" --reverse-user %s" % (n.reverse_username,)) - f.write(" --reverse-password %s" % (n.reverse_password,)) + f.write(" --reverse-user %s" % auth.reverse_username) + f.write(" --reverse-password %s" % auth.reverse_password) f.write("\n") def write(self, instPath, anaconda): -- cgit From e892c1599e9f8b2694a473bd709b0717aa5cb8b8 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 18 Mar 2009 16:52:08 +0100 Subject: Add a FIXME comment for setting uuid in VG / LV create Add a FIXME comment for setting uuid in VG / LV create, so that we do not forget this needs fixing somewhere in the future. --- storage/devices.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/storage/devices.py b/storage/devices.py index 0922778c3..dbfc9fae3 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -1621,6 +1621,7 @@ class LVMVolumeGroupDevice(DMDevice): self.createParents() self.setupParents() lvm.vgcreate(self.name, pv_list, self.peSize) + # FIXME set / update self.uuid here self.exists = True self.setup() @@ -1906,6 +1907,7 @@ class LVMLogicalVolumeDevice(DMDevice): # should we use --zero for safety's sake? lvm.lvcreate(self.vg.name, self._name, self.size) + # FIXME set / update self.uuid here self.exists = True self.setup() -- cgit From bd18c5370cacbac9a88074d8b0b66f1824c9cdb7 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 17 Mar 2009 11:28:51 -1000 Subject: Use booleans in isys.mount() and isys.umount() Use True/False over 1/0. It's the future. --- image.py | 12 ++++++------ isys/isys.py | 7 ++++--- livecd.py | 4 ++-- partedUtils.py | 6 ++++-- rescue.py | 4 ++-- storage/__init__.py | 2 +- yuminstall.py | 10 +++++----- 7 files changed, 24 insertions(+), 21 deletions(-) diff --git a/image.py b/image.py index 83e74dbb2..dea1e40d1 100644 --- a/image.py +++ b/image.py @@ -47,7 +47,7 @@ def findIsoImages(path, messageWindow): try: isys.mount("/dev/loop2", "/mnt/cdimage", fstype = "iso9660", - readOnly = 1) + readOnly = True) for num in range(1, 10): if os.access("/mnt/cdimage/.discinfo", os.R_OK): f = open("/mnt/cdimage/.discinfo") @@ -95,7 +95,7 @@ def findIsoImages(path, messageWindow): discImages[num] = file - isys.umount("/mnt/cdimage", removeDir=0) + isys.umount("/mnt/cdimage", removeDir=False) except SystemError: pass @@ -179,7 +179,7 @@ def mountImage(isodir, tree, discnum, messageWindow, discImages={}): try: isoImage = "%s/%s" % (isodir, discImages[discnum]) isys.losetup("/dev/loop1", isoImage, readOnly = 1) - isys.mount("/dev/loop1", tree, fstype = 'iso9660', readOnly = 1); + isys.mount("/dev/loop1", tree, fstype = 'iso9660', readOnly = True); break except: ans = messageWindow(_("Missing ISO 9660 Image"), @@ -255,7 +255,7 @@ def scanForMedia(tree, storage): continue try: - if isys.mount(dev.path, tree, fstype="iso9660", readOnly=1): + if isys.mount(dev.path, tree, fstype="iso9660", readOnly=True): continue except: continue @@ -270,7 +270,7 @@ def scanForMedia(tree, storage): def umountImage(tree, currentMedia): if currentMedia is not None: - isys.umount(tree, removeDir=0) + isys.umount(tree, removeDir=False) isys.unlosetup("/dev/loop1") def unmountCD(path, messageWindow): @@ -279,7 +279,7 @@ def unmountCD(path, messageWindow): while True: try: - isys.umount(path, removeDir=0) + isys.umount(path, removeDir=False) break except Exception, e: log.error("exception in _unmountCD: %s" %(e,)) diff --git a/isys/isys.py b/isys/isys.py index d2168dbf1..1f4f22286 100755 --- a/isys/isys.py +++ b/isys/isys.py @@ -130,7 +130,7 @@ def ddfile(file, megs, pw = None): os.close(fd) ## Mount a filesystem, similar to the mount system call. -# @param device The device to mount. If bindMount is 1, this should be an +# @param device The device to mount. If bindMount is True, this should be an # already mounted directory. Otherwise, it should be a device # name. # @param location The path to mount device on. @@ -141,7 +141,8 @@ def ddfile(file, megs, pw = None): # @param bindMount Is this a bind mount? (see the mount(8) man page) # @param remount Are we mounting an already mounted filesystem? # @return The return value from the mount system call. -def mount(device, location, fstype = "ext2", readOnly = 0, bindMount = 0, remount = 0, options = "defaults"): +def mount(device, location, fstype = "ext2", readOnly = False, + bindMount = False, remount = False, options = "defaults"): flags = None location = os.path.normpath(location) opts = string.split(options) @@ -180,7 +181,7 @@ def mount(device, location, fstype = "ext2", readOnly = 0, bindMount = 0, remoun # absolute path. # @param removeDir Should the mount point be removed after being unmounted? # @return The return value from the umount system call. -def umount(what, removeDir = 1): +def umount(what, removeDir = True): what = os.path.normpath(what) if not os.path.isdir(what): diff --git a/livecd.py b/livecd.py index 216b77eae..2d09717aa 100644 --- a/livecd.py +++ b/livecd.py @@ -140,7 +140,7 @@ class LiveCDCopyBackend(backend.AnacondaBackend): dirs.append("/selinux") for dir in dirs: try: - isys.umount("%s/%s" %(anaconda.rootPath,dir), removeDir = 0) + isys.umount("%s/%s" %(anaconda.rootPath,dir), removeDir = False) except Exception, e: log.error("unable to unmount %s: %s" %(dir, e)) @@ -303,7 +303,7 @@ class LiveCDCopyBackend(backend.AnacondaBackend): isys.mount("/selinux", anaconda.rootPath + "/selinux", "selinuxfs") except Exception, e: log.error("error mounting selinuxfs: %s" %(e,)) - isys.mount("/dev", "%s/dev" %(anaconda.rootPath,), bindMount = 1) + isys.mount("/dev", "%s/dev" %(anaconda.rootPath,), bindMount = True) wait.pop() diff --git a/partedUtils.py b/partedUtils.py index d0077f2b0..dde4d0b61 100644 --- a/partedUtils.py +++ b/partedUtils.py @@ -511,7 +511,8 @@ class DiskSet: fs = isys.readFSType(theDev) if fs is not None: try: - isys.mount(theDev, self.anaconda.rootPath, fs, readOnly = 1) + isys.mount(theDev, self.anaconda.rootPath, fs, + readOnly = True) found = 1 except SystemError: pass @@ -557,7 +558,8 @@ class DiskSet: fs = isys.readFSType(theDev) if fs is not None: try: - isys.mount(theDev, self.anaconda.rootPath, fs, readOnly = 1) + isys.mount(theDev, self.anaconda.rootPath, fs, + readOnly = True) found = 1 except SystemError: pass diff --git a/rescue.py b/rescue.py index ffdd6a822..aeceb9803 100644 --- a/rescue.py +++ b/rescue.py @@ -329,10 +329,10 @@ def runRescue(anaconda, instClass): log.error("Error enabling swap") # now that dev is udev, bind mount the installer dev there - isys.mount("/dev", "%s/dev" %(anaconda.rootPath,), bindMount = 1) + isys.mount("/dev", "%s/dev" %(anaconda.rootPath,), bindMount = True) # and /dev/pts - isys.mount("/dev/pts", "%s/dev/pts" %(anaconda.rootPath,), bindMount = 1) + isys.mount("/dev/pts", "%s/dev/pts" %(anaconda.rootPath,), bindMount = True) # and /selinux too if flags.selinux and os.path.isdir("%s/selinux" %(anaconda.rootPath,)): diff --git a/storage/__init__.py b/storage/__init__.py index 58f7fbf4f..ff5d99703 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1455,7 +1455,7 @@ class FSSet(object): def umountFilesystems(self, instPath, ignoreErrors=True, swapoff=True): # XXX if we tracked the /dev bind mount this wouln't be necessary if os.path.ismount("%s/dev" % instPath): - isys.umount("%s/dev" % instPath, removeDir=0) + isys.umount("%s/dev" % instPath, removeDir=False) devices = self.mountpoints.values() + self.swapDevices devices.extend([self.devshm, self.devpts, self.sysfs, self.proc]) diff --git a/yuminstall.py b/yuminstall.py index a5de70225..19f31be4c 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -329,7 +329,7 @@ class AnacondaYum(YumSorter): if self.currentMedia is None: try: isys.mount(self.anaconda.mediaDevice, self.tree, - fstype="iso9660", readOnly=1) + fstype="iso9660", readOnly=True) if verifyMedia(self.tree, discnum, None): self.currentMedia = discnum @@ -355,7 +355,7 @@ class AnacondaYum(YumSorter): try: isys.mount(self.anaconda.mediaDevice, self.tree, - fstype = "iso9660", readOnly = 1) + fstype = "iso9660", readOnly = True) if verifyMedia(self.tree, discnum, self._timestamp): self.currentMedia = discnum @@ -432,7 +432,7 @@ class AnacondaYum(YumSorter): # install instead. images = findIsoImages(self.tree, self.anaconda.intf.messageWindow) if images != {}: - isys.umount(self.tree, removeDir=0) + isys.umount(self.tree, removeDir=False) self.anaconda.methodstr = "nfsiso:%s" % m[4:] self.configBaseURL() return @@ -1367,7 +1367,7 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon if anaconda.dir == DISPATCH_BACK: for d in ("/selinux", "/dev"): try: - isys.umount(anaconda.rootPath + d, removeDir = 0) + isys.umount(anaconda.rootPath + d, removeDir = False) except Exception, e: log.error("unable to unmount %s: %s" %(d, e)) return @@ -1458,7 +1458,7 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon # handling /dev, it gets to be more fun. so just bind mount the # installer /dev log.warning("no dev package, going to bind mount /dev") - isys.mount("/dev", "%s/dev" %(anaconda.rootPath,), bindMount = 1) + isys.mount("/dev", "%s/dev" %(anaconda.rootPath,), bindMount = True) if not upgrade: anaconda.id.storage.fsset.mkDevRoot(anaconda.rootPath) -- cgit From 68db44e1c07a094e07ac4912f11b60a2592895f0 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 17 Mar 2009 11:29:55 -1000 Subject: Fix error message reading and writing in doPwMount() --- isys/imount.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/isys/imount.c b/isys/imount.c index e10220640..614e67ee6 100644 --- a/isys/imount.c +++ b/isys/imount.c @@ -146,9 +146,9 @@ int doPwMount(char *dev, char *where, char *fs, char *options, char **err) { close(pipefd[1]); - if (err != NULL) { + if (*err != NULL) { rc = readFD(pipefd[0], err); - rc = write(programLogFD, err, 4096); + rc = write(programLogFD, *err, 4096); } close(pipefd[0]); -- cgit From 311a818539bb36af2bb0821190d8cbab9db04170 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 17 Mar 2009 11:31:03 -1000 Subject: Place all mount.* commands in /sbin This is required for 'mount -t TYPE SOURCE MOUNTPOINT' to work. NTFS is supported through fuse, so we don't have it listed in /proc/filesystems, but if /sbin/mount.ntfs exists, then we can mount ntfs like any other filesystem. --- scripts/mk-images | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/scripts/mk-images b/scripts/mk-images index d4cac465f..db2fa9df0 100755 --- a/scripts/mk-images +++ b/scripts/mk-images @@ -598,9 +598,15 @@ makeinitrd() { install -m 644 $IMGPATH/etc/nsswitch.conf $MBD_DIR/etc/nsswitch.conf instbin $IMGPATH /usr/bin/mount $MBD_DIR /sbin/mount - instbin $IMGPATH /usr/sbin/mount.nfs $MBD_DIR /sbin/mount.nfs + for mountcmd in $IMGPATH/usr/sbin/mount.* ; do + cmd="$(basename $mountcmd)" + instbin $IMGPATH /usr/sbin/$cmd $MBD_DIR /sbin/$cmd + done instbin $IMGPATH /usr/bin/umount $MBD_DIR /sbin/umount - ln -s mount.nfs $MBD_DIR/sbin/umount.nfs + for umountcmd in $IMGPATH/usr/sbin/umount.* ; do + cmd="$(basename $umountcmd)" + instbin $IMGPATH /usr/sbin/$cmd $MBD_DIR /sbin/$cmd + done instbin $IMGPATH /usr/sbin/udevd $MBD_DIR /sbin/udevd instbin $IMGPATH /usr/sbin/udevadm $MBD_DIR /sbin/udevadm -- cgit From ff0de7c1875788ac69052e091c2aac53de8fb7bb Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 17 Mar 2009 12:28:39 -1000 Subject: Fix some errors in the updates target. Nothing critical, wipe subdirs before copying new ones in. Use the case block to skip of subdirs we want to ignore. Don't use dirname, but just cut the first field in $sourcefile. --- Makefile | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index b2a75c75b..2555181f6 100644 --- a/Makefile +++ b/Makefile @@ -184,14 +184,16 @@ updates: mkdir updates-img ; \ fi ; \ git diff --stat $(ARCHIVE_TAG) | grep " | " | \ - grep -v "^\ loader\/" | grep -v "\.spec" | grep -v "Makefile" | \ - grep -v "^\ po\/" | grep -v "^\ scripts\/" | \ + grep -v "\.spec" | grep -v "Makefile" | grep -v "\.c\ " | \ while read sourcefile stuff ; do \ - dn="$$(dirname $$sourcefile)" ; \ + dn="$$(echo $$sourcefile | cut -d '/' -f 1)" ; \ case $$dn in \ installclasses|storage|booty) \ + rm -rf updates-img/$$dn ; \ cp -a $$dn updates-img ; \ find updates-img/$$dn -type f | grep Makefile | xargs rm -f ;; \ + loader|po|scripts|command-stubs|tests|bootdisk|docs|fonts|utils|gptsync) \ + continue ;; \ *) \ cp -a $$sourcefile updates-img ;; \ esac ; \ -- cgit From 992306b96a3ae97aac04606a753d9312043877ba Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 17 Mar 2009 13:40:02 -1000 Subject: Mount existing filesystems read-only when getting size. Pass 'ro' in the mount options. Also add a hyphen in the temporary mount point name so it looks like 'getsize-XXXXXX'. --- storage/formats/fs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index ac2d9b518..a32c65ba1 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -202,8 +202,8 @@ class FS(DeviceFormat): if self.mountable: origMountPoint = self._mountpoint - tmppath = tempfile.mkdtemp(prefix='getsize', dir='/tmp') - self.mount(mountpoint=tmppath) + tmppath = tempfile.mkdtemp(prefix='getsize-', dir='/tmp') + self.mount(mountpoint=tmppath, options="ro") buf = os.statvfs(tmppath) self.unmount() -- cgit From 45cdcdc4d0b233a4ab0c91d9c88dd7eaa7e7355e Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 17 Mar 2009 13:41:09 -1000 Subject: Indicate filesystem is mountable if we have a mount command. For NTFS, we don't have the name 'ntfs' in /proc/filesystems. It uses fuseblk. Return True for mountable if the filesystem is listed in /proc/filesystems -or- if we have a mount command for that filesystem type. --- storage/formats/fs.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index a32c65ba1..71160bb7c 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -594,7 +594,8 @@ class FS(DeviceFormat): @property def mountable(self): - return self.type in kernel_filesystems + return (self.type in kernel_filesystems) or \ + (os.access("/sbin/mount.%s" % (self.type,), os.X_OK)) @property def defaultFormatOptions(self): -- cgit From c97de90819f7d41537e8039cde63fd8409350548 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 19 Mar 2009 02:05:23 -0500 Subject: Try to activate an existing md array after adding each member. Once the array has been activated the new members should get added to the active array as we add them to the MDRaidArrayDevice. We just need to start the array for that to happen. --- storage/devicetree.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/storage/devicetree.py b/storage/devicetree.py index 850a22039..f7582aced 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1181,6 +1181,11 @@ class DeviceTree(object): uuid=md_uuid, exists=True, parents=[device]) + try: + md_array.setup() + except (DeviceError, MDRaidError) as e: + log.info("setup of md array %s failed: %s" + % (md_array.name, e)) self._addDevice(md_array) elif format.type == "dmraidmember": major = udev_device_get_major(info) -- cgit From e5371fffaee326c0e2132a97441c50ca032ced89 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 19 Mar 2009 02:08:06 -0500 Subject: Devices should not be resizable unless they exist. --- storage/devices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devices.py b/storage/devices.py index dbfc9fae3..1a52c7119 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -458,7 +458,7 @@ class StorageDevice(Device): @property def resizable(self): """ Can this type of device be resized? """ - return self._resizable + return self._resizable and self.exists def notifyKernel(self): """ Send a 'change' uevent to the kernel for this device. """ -- cgit From 9abfdc92a55618e6b2994c19fede8316405fb832 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 19 Mar 2009 02:10:48 -0500 Subject: Fix pruning of destroy actions for preexisting devices. If a device is preexisting, we should prune all actions on that device up through the last destroy action. --- storage/devicetree.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/storage/devicetree.py b/storage/devicetree.py index f7582aced..2b997b58e 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -223,6 +223,11 @@ class DeviceTree(object): # this device is not preexisting start = first_create_idx stop_action = destroys[-1] + else: + # no create actions means this is a preexisting device, + # so we prune all actions up through the last destroy. + start = 0 + stop_action = destroys[-1] if start is None: continue -- cgit From b75e968ed2e9a1b3c23a26348913a4fca6f60c8d Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 19 Mar 2009 02:13:03 -0500 Subject: Improve chances of uniqueness from Storage.createSuggestedLVName. --- storage/__init__.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index ff5d99703..7b7ab5c68 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -649,6 +649,7 @@ class Storage(object): def createSuggestedLVName(self, vg, swap=None, mountpoint=None): """ Return a suitable, unused name for a new logical volume. """ + # FIXME: this is not at all guaranteed to work if mountpoint: # try to incorporate the mountpoint into the name if mountpoint == '/': @@ -658,12 +659,24 @@ class Storage(object): lvtemplate = "lv_%s" % (tmp,) else: if swap: - if len(self.swaps): - lvtemplate = "lv_swap%02d" % (len(self.swaps),) + if len([s for s in self.swaps if s in vg.lvs]): + idx = len([s for s in self.swaps if s in vg.lvs]) + while True: + lvtemplate = "lv_swap%02d" % idx + if lvtemplate in [lv.lvname for lv in vg.lvs]: + idx += 1 + else: + break else: lvtemplate = "lv_swap" else: - lvtemplate = "LogVol%02d" % (len(vg.lvs),) + idx = len(vg.lvs) + while True: + lvtemplate = "LogVol%02d" % idx + if lvtemplate in [l.lvname for l in vg.lvs]: + idx += 1 + else: + break return lvtemplate -- cgit From 198044e9785dd4e5eeb123760b4b35df4ea4cd36 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 19 Mar 2009 03:58:29 -0500 Subject: Rework the lvm dialog. (#490301,#490966,#490681,#489870) I opted to maintain the lv information in a simple dict which we convert to a temporary vg/lv instance set when we need to. There is no trampling on objects, and the code is just simpler. The big difference is that we have to go through and figure out what all was done at the very end, right before returning to the partition gui. So far, it is considerably more stable than the previous implementation was. --- iw/lvm_dialog_gui.py | 601 ++++++++++++++++++++++++++++++++------------------- 1 file changed, 377 insertions(+), 224 deletions(-) diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index be3e246b3..5b07b5807 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -40,12 +40,22 @@ log = logging.getLogger("anaconda") class VolumeGroupEditor: + def getTempVG(self): + pvs = [copy.deepcopy(pv) for pv in self.pvs] + vg = LVMVolumeGroupDevice('tmp-%s' % self.vg.name, + parents=pvs, peSize=self.peSize) + for lv in self.lvs.values(): + LVMLogicalVolumeDevice(lv['name'], vg, format=lv['format'], + size=lv['size'], exists=lv['exists']) + return vg + def numAvailableLVSlots(self): - return max(0, lvm.MAX_LV_SLOTS - len(self.vg.lvs)) + return max(0, lvm.MAX_LV_SLOTS - len(self.lvs)) def computeSpaceValues(self): - vgsize = self.vg.size - vgfree = self.vg.freeSpace + vg = self.getTempVG() + vgsize = vg.size + vgfree = vg.freeSpace vgused = vgsize - vgfree return (vgsize, vgused, vgfree) @@ -54,7 +64,7 @@ class VolumeGroupEditor: newpe - (int) new value of PE, in KB """ - pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model()) + pvlist = self.getSelectedPhysicalVolumes() waste = 0.0 for pv in pvlist: @@ -66,7 +76,7 @@ class VolumeGroupEditor: """ finds the smallest PV and returns its size in MB """ first = 1 - pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model()) + pvlist = self.getSelectedPhysicalVolumes() for pv in pvlist: try: pesize = int(self.peCombo.get_active_value()) @@ -90,15 +100,15 @@ class VolumeGroupEditor: newpe - (int) new value of PE, in KB """ - pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model()) + pvlist = self.getSelectedPhysicalVolumes() availSpaceMB = self.computeVGSize(pvlist, newpe) # see if total space is enough oldused = 0 used = 0 resize = 0 - for lv in self.vg.lvs: - osize = lv.size + for lv in self.lvs.values(): + osize = lv['size'] oldused = oldused + osize nsize = lvm.clampSize(osize, newpe, roundup=1) if nsize != osize: @@ -131,12 +141,8 @@ class VolumeGroupEditor: if not rc: return 0 - # XXX this is very sneaky, just changing the lvs' size attributes. - # will we suffer for it? almost certainly. - for lv in self.vg.lvs: - osize = lv.size - nsize = lvm.clampSize(osize, newpe, roundup=1) - lv.size = nsize + for lv in self.lvs.values(): + lv['size'] = lvm.clampSize(lv['size'], newpe, roundup=1) return 1 @@ -168,7 +174,7 @@ class VolumeGroupEditor: return 0 # see if new PE will make any PV useless due to overhead - if lvm.clampPVSize(maxpvsize, curpe) < curpe: + if lvm.clampSize(maxpvsize, curpe) < curpe: self.intf.messageWindow(_("Not enough space"), _("The physical extent size cannot be " "changed because the value selected " @@ -206,8 +212,8 @@ class VolumeGroupEditor: self.updateLogVolStore() else: maxlv = lvm.getMaxLVSize() - for lv in self.vg.lvs: - if lv.size > maxlv: + for lv in self.lvs.values(): + if lv['size'] > maxlv: self.intf.messageWindow(_("Not enough space"), _("The physical extent size " "cannot be changed because the " @@ -225,9 +231,8 @@ class VolumeGroupEditor: widget.set_data("lastidx", widget.get_active()) # now actually set the VG's extent size - self.vg.peSize = curpe - self.updateAllowedLvmPartitionsList(self.availlvmparts, - self.lvmlist) + self.peSize = curpe + self.updateAllowedLvmPartitionsList() self.updateVGSpaceLabels() def prettyFormatPESize(self, val): @@ -268,7 +273,7 @@ class VolumeGroupEditor: def clickCB(self, row, data): model = self.lvmlist.get_model() - pvlist = self.getSelectedPhysicalVolumes(model) + pvlist = self.getSelectedPhysicalVolumes() # get the selected row iter = model.get_iter((string.atoi(data),)) @@ -278,16 +283,12 @@ class VolumeGroupEditor: val = not model.get_value(iter, 0) partname = model.get_value(iter, 1) pv = self.storage.devicetree.getDeviceByName(partname) - if val: - pvlist.append(pv) - else: - pvlist.remove(pv) - if val: - self.vg._addPV(pv) + self.pvs.append(pv) else: + self.pvs.remove(pv) try: - self.vg._removePV(pv) + vg = self.getTempVG() except DeviceError as e: self.intf.messageWindow(_("Not enough space"), _("You cannot remove this physical " @@ -295,13 +296,13 @@ class VolumeGroupEditor: "volume group will be too small to " "hold the currently defined logical " "volumes."), custom_icon="error") + self.pvs.append(pv) return False self.updateVGSpaceLabels() return True - - def createAllowedLvmPartitionsList(self, alllvmparts, vgs): + def createAllowedLvmPartitionsList(self): store = gtk.TreeStore(gobject.TYPE_BOOLEAN, gobject.TYPE_STRING, gobject.TYPE_STRING) @@ -312,7 +313,8 @@ class VolumeGroupEditor: sw.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) sw.set_shadow_type(gtk.SHADOW_IN) - for device in alllvmparts: + origpvs = self.pvs[:] + for device in self.availlvmparts: # clip size to current PE pesize = int(self.peCombo.get_active_value()) / 1024.0 size = lvm.clampSize(device.size, pesize) @@ -322,11 +324,11 @@ class VolumeGroupEditor: # now see if the pv is in use either by a vg in the tree or by # the vg we are editing now - if device in self.vg.pvs: + if device in origpvs: selected = True include = True else: - for vg in vgs: + for vg in self.storage.vgs: if vg.name == self.vg.name: continue @@ -334,25 +336,20 @@ class VolumeGroupEditor: include = False break - if include and not self.vg.pvs: + if include and not origpvs: selected = True if include: partlist.append_row((device.name, size_string), selected) - if selected and device not in self.vg.pvs: - self.vg._addPV(device) + if selected and device not in self.pvs: + self.pvs.append(device) return (partlist, sw) - def updateAllowedLvmPartitionsList(self, alllvmparts, partlist): - """ update sizes in pv list - - alllvmparts - list of pv from partitions.getAvailLVMPartitions - partlist - the checklist containing pv list - """ - + def updateAllowedLvmPartitionsList(self): + """ update sizes in pv list """ row = 0 - for part in alllvmparts: + for part in self.availlvmparts: size = part.size # clip size to current PE @@ -360,8 +357,8 @@ class VolumeGroupEditor: size = lvm.clampSize(size, pesize) partsize = "%10.2f MB" % size - iter = partlist.store.get_iter((int(row),)) - partlist.store.set_value(iter, 2, partsize) + iter = self.lvmlist.store.get_iter((int(row),)) + self.lvmlist.store.set_value(iter, 2, partsize) row = row + 1 def getCurrentLogicalVolume(self): @@ -370,17 +367,11 @@ class VolumeGroupEditor: return iter def editLogicalVolume(self, lv, isNew = 0): - if not lv: - return - if isNew: tstr = _("Make Logical Volume") else: - try: - tstr = _("Edit Logical Volume: %s") % (lv.name,) - except AttributeError: - tstr = _("Edit Logical Volume") - + tstr = _("Edit Logical Volume: %s") % lv['name'] + dialog = gtk.Dialog(tstr, self.parent) gui.addFrame(dialog) dialog.add_button('gtk-cancel', 2) @@ -392,14 +383,26 @@ class VolumeGroupEditor: maintable.set_col_spacings(5) row = 0 - if lv.format.type == "luks": - luksdev = self.findLUKSDev(lv) - usedev = luksdev - format = usedev.format - elif lv: - luksdev = None - usedev = lv - format = lv.format + tempvg = self.getTempVG() + templv = None + templuks = None + usedev = None + for _lv in tempvg.lvs: + if _lv.lvname == lv['name']: + templv = _lv + usedev = templv + if templv.format.type == "luks": + templuks = LUKSDevice("luks-%s" % lv['name'], + parents=[templv], + format=self.luks[lv['name']], + exists=templv.format.exists) + usedev = templuks + break + + if lv['format'].type == "luks": + format = self.luks[lv['name']] + else: + format = lv['format'] lbl = createAlignedLabel(_("_Mount Point:")) maintable.attach(lbl, 0, 1, row,row+1) @@ -408,13 +411,13 @@ class VolumeGroupEditor: maintable.attach(mountCombo, 1, 2, row, row + 1) row += 1 - if not format.exists: + if not lv['exists']: lbl = createAlignedLabel(_("_File System Type:")) maintable.attach(lbl, 0, 1, row, row + 1) newfstypeCombo = createFSTypeMenu(format, fstypechangeCB, mountCombo, - ignorefs = ["software RAID", "physical volume (LVM)", "vfat", "efi", "PPC PReP Boot", "Apple Bootstrap"]) + ignorefs = ["software RAID", "physical volume (LVM)", "vfat", "PPC PReP Boot", "hfs"]) lbl.set_mnemonic_widget(newfstypeCombo) maintable.attach(newfstypeCombo, 1, 2, row, row + 1) row += 1 @@ -437,49 +440,61 @@ class VolumeGroupEditor: 1, 2, row, row + 1) row += 1 - if not lv.exists: + if not lv['exists']: lbl = createAlignedLabel(_("_Logical Volume Name:")) lvnameEntry = gtk.Entry(32) lbl.set_mnemonic_widget(lvnameEntry) - if lv and lv.lvname: - lvnameEntry.set_text(lv.lvname) + if lv['name']: + lvnameEntry.set_text(lv['name']) else: - lvnameEntry.set_text(storage.createSuggestedLVName(self.vg)) + lvnameEntry.set_text(storage.createSuggestedLVName(self.getTempVG())) else: lbl = createAlignedLabel(_("Logical Volume Name:")) - lvnameEntry = gtk.Label(lv.lvname) + lvnameEntry = gtk.Label(lv['name']) maintable.attach(lbl, 0, 1, row, row + 1) maintable.attach(lvnameEntry, 1, 2, row, row + 1) row += 1 - if not lv.exists: + if not lv['exists']: lbl = createAlignedLabel(_("_Size (MB):")) sizeEntry = gtk.Entry(16) lbl.set_mnemonic_widget(sizeEntry) - sizeEntry.set_text("%Ld" % lv.size) + sizeEntry.set_text("%Ld" % lv['size']) else: lbl = createAlignedLabel(_("Size (MB):")) - sizeEntry = gtk.Label(str(lv.size)) + sizeEntry = gtk.Label(str(lv['size'])) maintable.attach(lbl, 0, 1, row, row+1) maintable.attach(sizeEntry, 1, 2, row, row + 1) row += 1 - if not lv.exists: - maxlv = min(lvm.getMaxLVSize(), lv.size) + if not lv['exists']: + maxlv = min(lvm.getMaxLVSize(), lv['size']) maxlabel = createAlignedLabel(_("(Max size is %s MB)") % (maxlv,)) maintable.attach(maxlabel, 1, 2, row, row + 1) self.fsoptionsDict = {} - if lv.exists: - (row, self.fsoptionsDict) = createPreExistFSOptionSection(lv, maintable, row, mountCombo, self.storage, ignorefs = ["software RAID", "physical volume (LVM)", "vfat"], luksdev=luksdev) + if lv['exists']: + templuks = None + reallv = None + for _lv in self.vg.lvs: + if _lv.lvname == lv['name']: + reallv = _lv + if _lv.format.type == "luks": + try: + templuks = self.storage.devicetree.getChildren(_lv)[0] + except IndexError: + templuks = None + break + + (row, self.fsoptionsDict) = createPreExistFSOptionSection(reallv, maintable, row, mountCombo, self.storage, ignorefs = ["software RAID", "physical volume (LVM)", "vfat"], luksdev=templuks) # checkbutton for encryption using dm-crypt/LUKS - if not lv.exists: + if not lv['exists']: self.lukscb = gtk.CheckButton(_("_Encrypt")) self.lukscb.set_data("formatstate", 1) - if lv.format.type == "luks": + if lv['format'].type == "luks": self.lukscb.set_active(1) else: self.lukscb.set_active(0) @@ -502,15 +517,14 @@ class VolumeGroupEditor: targetSize = None migrate = None format = None + newluks = None - if lv.format.type == "luks": - usedev = self.findLUKSDev(lv) - format = usedev.format + if templv.format.type == "luks": + format = self.luks[lv['name']] else: - usedev = lv - format = lv.format + format = templv.format - if not lv.exists: + if not templv.exists: fmt_class = newfstypeCombo.get_active_value() else: # existing lv @@ -520,7 +534,7 @@ class VolumeGroupEditor: # validate logical volume name lvname = lvnameEntry.get_text().strip() - if not lv.exists: + if not templv.exists: err = sanityCheckLogicalVolumeName(lvname) if err: self.intf.messageWindow(_("Illegal Logical Volume Name"), @@ -529,11 +543,11 @@ class VolumeGroupEditor: # check that the name is not already in use used = 0 - for _lv in self.vg.lvs: - if lvname and lv.lvname == lvname: + for _lv in self.lvs.values(): + if _lv == lv: continue - if lv.lvname == lvname: + if _lv['name'] == lvname: used = 1 break @@ -552,15 +566,11 @@ class VolumeGroupEditor: used = 0 curmntpt = getattr(format, "mountpoint", None) - for _lv in self.vg.lvs: - if _lv.format.type == "luks": - # XXX this won't work if the lvs haven't been - # added to the tree yet - _usedev = self.findLUKSDev(_lv) - _format = _usedev.format + for _lv in self.lvs.values(): + if _lv['format'].type == "luks": + _format = self.luks[_lv['name']] else: - _usedev = _lv - _format = _lv.format + _format = _lv['format'] if not _format.mountable or curmntpt and \ _format.mountpoint == curmntpt: @@ -579,7 +589,7 @@ class VolumeGroupEditor: continue # check that size specification is numeric and positive - if not lv.exists: + if not templv.exists: badsize = 0 try: size = long(sizeEntry.get_text()) @@ -593,7 +603,7 @@ class VolumeGroupEditor: "than 0."), custom_icon="error") continue else: - size = lv.size + size = templv.size # check that size specification is within limits pesize = int(self.peCombo.get_active_value()) / 1024.0 @@ -614,93 +624,70 @@ class VolumeGroupEditor: # Ok -- now we've done all the checks to validate the # user-specified parameters. Time to set up the device... - if not lv.exists: - lv._name = lvname - lv.size = size - lv.req_grow = False + origname = templv.lvname + if not templv.exists: + templv._name = lvname + templv.size = size format = fmt_class(mountpoint=mountpoint) if self.lukscb and self.lukscb.get_active() and \ - lv.format.type != "luks": - luksformat = format + templv.format.type != "luks": + newluks = format format = getFormat("luks", passphrase=self.storage.encryptionPassphrase) - luksdev = LUKSDevice("luks-%s" % lv.name, - format=luksformat, - parents=lv) - elif self.lukscb and not self.lukscb.get_active() and \ - lv.format.type == "luks": - # destroy luks format and mapped device - luksdev = self.findLUKSDev(lv) - if luksdev: - actions.append(ActionDestroyFormat(luksdev.format)) - actions.append(ActionDestroyDevice(luksdev)) - luksdev = None - actions.append(ActionDestroyFormat(lv)) - - if lv.lvname not in self.lvs: - actions.append(ActionCreateDevice(lv)) - actions.append(ActionCreateFormat(lv)) - self.lvs[lv.lvname] = lv - - if luksdev: - actions.append(ActionCreateDevice(luksdev)) - actions.append(ActionCreateFormat(luksdev)) + templv.format = format else: # existing lv if self.fsoptionsDict.has_key("formatcb") and \ self.fsoptionsDict["formatcb"].get_active(): format = fmt_class(mountpoint=mountpoint) if self.lukscb and self.lukscb.get_active() and \ - lv.format.type != "luks": - luksformat = format + templv.format.type != "luks": + newluks = format format = getFormat("luks", - device=lv.path, + device=templv.path, passphrase=self.storage.encryptionPassphrase) - luksdev = LUKSDevice("luks-%s" % lv.name, - format=luksformat, - parents=lv) - elif self.lukscb and not self.lukscb.get_active() and \ - lv.format.type == "luks": - # destroy luks format and mapped device - luksdev = self.findLUKSDev(lv) - if luksdev: - actions.append(ActionDestroyFormat(luksdev.format)) - actions.append(ActionDestroyDevice(luksdev)) - luksdev = None - actions.append(ActionDestroyFormat(lv)) - elif lv.format.mountable: - lv.format.mountpoint = mountpoint + + templv.format = format + elif format.mountable: + templv.format.mountpoint = mountpoint if self.fsoptionsDict.has_key("migratecb") and \ self.fsoptionsDict["migratecb"].get_active(): - migrate = True + format.migrate = True if self.fsoptionsDict.has_key("resizecb") and self.fsoptionsDict["resizecb"].get_active(): targetSize = self.fsoptionsDict["resizesb"].get_value_as_int() - actions.append(ActionResizeDevice(lv, targetSize)) - if lv.format.type: - actions.append(ActionResizeFormat(lv, targetSize)) - + templv.targetSize = targetSize + format.targetSize = targetSize - if format: - actions.append(ActionDestroyFormat(usedev)) - actions.append(ActionCreateFormat(usedev, format)) - if luksdev: - actions.append(ActionCreateDevice(luksdev)) - actions.append(ActionCreateFormat(luksdev)) + templv.format = format - if migrate: - actions.append(ActionMigrateFormat(usedev)) - - if usedev.format.exists and format.mountable and \ - self.storage.formatByDefault(usedev) and \ - not queryNoFormatPreExisting(self.intf): + if format.exists and format.mountable and format.mountpoint: + tempdev = StorageDevice('tmp', format=format) + if self.storage.formatByDefault(tempdev) and \ + not queryNoFormatPreExisting(self.intf): continue # everything ok break - self.actions.extend(actions) + if templv.format.type == "luks": + if newluks: + self.luks[templv.lvname] = newluks + + if self.luks.has_key(origname) and origname != templv.lvname: + self.luks[templv.lvname] = self.luks[origname] + del self.luks[templv.lvname] + elif templv.format.type != "luks" and self.luks.has_key(origname): + del self.luks[origname] + + self.lvs[templv.lvname] = {'name': templv.lvname, + 'size': templv.size, + 'format': templv.format, + 'exists': templv.exists} + if self.lvs.has_key(origname) and origname != templv.lvname: + del self.lvs[origname] + self.updateLogVolStore() self.updateVGSpaceLabels() dialog.destroy() @@ -734,12 +721,15 @@ class VolumeGroupEditor: "the currently existing " "logical volumes"), custom_icon="error") return - - lv = self.storage.newLV(vg=self.vg, - fmt_type=self.storage.defaultFSType, - size=free) - self.editLogicalVolume(lv, isNew = 1) - return + + tempvg = self.getTempVG() + name = self.storage.createSuggestedLVName(tempvg) + self.lvs[name] = {'name': name, + 'size': free, + 'format': getFormat(self.storage.defaultFSType), + 'exists': False} + self.editLogicalVolume(self.lvs[name], isNew = 1) + return def editLogicalVolumeCB(self, widget): self.editCurrentLogicalVolume() @@ -761,21 +751,20 @@ class VolumeGroupEditor: if not rc: return - lv = self.lvs[logvolname] - action = ActionDestroyDevice(lv) - self.actions.append(action) - self.logvolstore.remove(iter) - self.updateVGSpaceLabels() - return + del self.lvs[logvolname] + self.logvolstore.remove(iter) + self.updateVGSpaceLabels() + return def logvolActivateCb(self, view, path, col): self.editCurrentLogicalVolume() - def getSelectedPhysicalVolumes(self, model): - pv = [] - next = model.get_iter_first() - currow = 0 - while next is not None: + def getSelectedPhysicalVolumes(self): + model = self.lvmlist.get_model() + pv = [] + next = model.get_iter_first() + currow = 0 + while next is not None: iter = next val = model.get_value(iter, 0) partname = model.get_value(iter, 1) @@ -792,10 +781,10 @@ class VolumeGroupEditor: def computeVGSize(self, pvlist, curpe): availSpaceMB = 0L for pv in pvlist: - # XXX why the subtraction? + # have to clamp pvsize to multiple of PE + # XXX why the subtraction? fudging metadata? pvsize = lvm.clampSize(pv.size, curpe) - (curpe/1024) - # have to clamp pvsize to multiple of PE availSpaceMB = availSpaceMB + pvsize log.info("computeVGSize: vgsize is %s" % (availSpaceMB,)) @@ -808,40 +797,25 @@ class VolumeGroupEditor: return neededSpaceMB - def findLUKSDev(self, lv): - if lv.format.type == "luks": - actions = self.actions[:] - actions.reverse() - usedev = None - for action in actions: - if action.isCreate() and action.isDevice() and \ - action.device.parents == [lv]: - usedev = action.device - break - if not usedev: - usedev = self.storage.devicetree.getChildren(lv)[0] - - return usedev - def updateLogVolStore(self): self.logvolstore.clear() - for lv in self.vg.lvs: + for lv in self.lvs.values(): iter = self.logvolstore.append() - if lv.format.type == "luks": - usedev = self.findLUKSDev(lv) + if lv['format'].type == "luks": + format = self.luks[lv['name']] else: - usedev = lv + format = lv['format'] - mntpt = getattr(usedev.format, "mountpoint", "") - if lv.lvname: - self.logvolstore.set_value(iter, 0, lv.lvname) + mntpt = getattr(format, "mountpoint", "") + if lv['name']: + self.logvolstore.set_value(iter, 0, lv['name']) - if usedev.format.type and usedev.format.mountable: + if format.type and format.mountable: self.logvolstore.set_value(iter, 1, mntpt) else: self.logvolstore.set_value(iter, 1, "N/A") - self.logvolstore.set_value(iter, 2, "%Ld" % (lv.size,)) + self.logvolstore.set_value(iter, 2, "%Ld" % lv['size']) def updateVGSpaceLabels(self): (total, used, free) = self.computeSpaceValues() @@ -878,7 +852,7 @@ class VolumeGroupEditor: self.destroy() return [] - pvlist = self.getSelectedPhysicalVolumes(self.lvmlist.get_model()) + pvlist = self.getSelectedPhysicalVolumes() # check volume name volname = self.volnameEntry.get_text().strip() @@ -888,10 +862,7 @@ class VolumeGroupEditor: custom_icon="error") continue - if self.vg: - origvname = self.vg.name - else: - origvname = None + origvname = self.vg.name if origvname != volname: # maybe we should see if _any_ device has this name @@ -909,12 +880,172 @@ class VolumeGroupEditor: # everything ok break - # pvs, pesize are taken care of in widget callbacks - # (clickCB, peChangeCB) - if self.isNew: + # here we have to figure out what all was done and convert it to + # devices and actions + # + # set up the vg with the right pvs + # set up the lvs + # set up the lvs' formats + # + log.debug("finished editing vg") + log.debug("pvs: %s" % [p.name for p in self.pvs]) + log.debug("luks: %s" % self.luks.keys()) + for lv in self.lvs.itervalues(): + log.debug("lv %s" % lv) + _luks = self.luks.get(lv['name']) + if _luks: + log.debug(" luks: %s" % _luks) + + actions = [] + origlvs = self.vg.lvs + if not self.vg.exists: + log.debug("non-existing vg -- setting up lvs, pvs, name, pesize") + # remove all of the lvs + for lv in self.vg.lvs: + self.vg._removeLogVol(lv) + + # set up the pvs + for pv in self.vg.pvs: + if pv not in self.pvs: + self.vg._removePV(pv) + for pv in self.pvs: + if pv not in self.vg.pvs: + self.vg._addPV(pv) + self.vg.name = volname - self.actions.insert(0, ActionCreateDevice(self.vg)) - return self.actions + self.vg.pesize = pesize + + if self.isNew: + actions = [ActionCreateDevice(self.vg)] + + # Schedule destruction of all non-existing lvs, their formats, + # luks devices, &c. Also destroy devices that have been removed. + for lv in origlvs: + log.debug("old lv %s..." % lv.lvname) + if not lv.exists or lv.lvname not in self.lvs or \ + (not self.lvs[lv.lvname]['exists'] and lv.exists): + log.debug("removing lv %s" % lv.lvname) + if lv.format.type == "luks": + try: + _luksdev = self.storage.devicetree.getChildren(lv)[0] + except IndexError: + pass + else: + if _luksdev.format.type: + actions.append(ActionDestroyFormat(_luksdev)) + + actions.append(ActionDestroyDevice(_luksdev)) + + if lv.format.type: + actions.append(ActionDestroyFormat(lv)) + + if lv in self.vg.lvs: + self.vg._removeLogVol(lv) + + actions.append(ActionDestroyDevice(lv)) + + # schedule creation of all new lvs, their formats, luks devices, &c + tempvg = self.getTempVG() + for lv in tempvg.lvs: + log.debug("new lv %s" % lv) + if not lv.exists: + log.debug("creating lv %s" % lv.lvname) + # create the device + newlv = LVMLogicalVolumeDevice(lv.lvname, + self.vg, + size=lv.size) + actions.append(ActionCreateDevice(newlv)) + + # create the format + mountpoint = getattr(lv.format, "mountpoint", None) + format = getFormat(lv.format.type, + mountpoint=mountpoint, + device=newlv.path) + actions.append(ActionCreateFormat(newlv, format)) + + if lv.format.type == "luks": + # create the luks device + newluks = LUKSDevice("luks-%s" % newlv.name, + parents=[newlv]) + actions.append(ActionCreateDevice(newluks)) + + # create the luks format + oldfmt = self.luks[lv.lvname] + mountpoint = getattr(oldfmt, "mountpoint", None) + format = getFormat(oldfmt.type, + mountpoint=mountpoint, + device=newluks.path) + actions.append(ActionCreateFormat(newluks, format)) + else: + log.debug("lv %s already exists" % lv.lvname) + # this lv is preexisting. check for resize and reformat. + # first, get the real/original lv + origlv = None + for _lv in self.vg.lvs: + if _lv.lvname == lv.lvname: + origlv = _lv + break + + if lv.resizable and lv.targetSize != origlv.currentSize: + actions.append(ActionResizeDevice(origlv, lv.targetSize)) + + if lv.format.exists: + log.debug("format already exists") + if hasattr(origlv.format, "mountpoint"): + origlv.format.mountpoint = lv.format.mountpoint + + if lv.format.migratable and lv.format.migrate and \ + not origlv.format.migrate: + origlv.format.migrate = lv.format.migrate + actions.append(ActionMigrateFormat(origlv)) + + if lv.format.resizable and \ + lv.format.targetSize != lv.format.currentSize: + new_size = lv.format.targetSize + actions.append(ActionResizeFormat(origlv, new_size)) + elif lv.format.type: + log.debug("new format: %s" % lv.format.type) + # destroy old format and any associated luks devices + if origlv.format.type: + if origlv.format.type == "luks": + # destroy the luks device and its format + try: + _luksdev = self.storage.devicetree.getChildren(origlv)[0] + except IndexError: + pass + else: + if _luksdev.format.type: + # this is probably unnecessary + actions.append(ActionDestroyFormat(_luksdev)) + + actions.append(ActionDestroyDevice(_luksdev)) + + actions.append(ActionDestroyFormat(origlv)) + + # create the format + mountpoint = getattr(lv.format, "mountpoint", None) + format = getFormat(lv.format.type, + mountpoint=mountpoint, + device=origlv.path) + actions.append(ActionCreateFormat(origlv, format)) + + if lv.format.type == "luks": + # create the luks device + newluks = LUKSDevice("luks-%s" % origlv.name, + parents=[origlv]) + actions.append(ActionCreateDevice(newluks)) + + # create the luks format + tmpfmt = self.luks[lv.lvname] + mountpoint = getattr(tmpfmt, "mountpoint", None) + format = getFormat(tmpfmt.type, + mountpoint=mountpoint, + device=newluks.path) + actions.append(ActionCreateFormat(newluks, format)) + else: + log.debug("no format!?") + + return actions def destroy(self): if self.dialog: @@ -922,16 +1053,40 @@ class VolumeGroupEditor: self.dialog = None def __init__(self, anaconda, intf, parent, vg, isNew = 0): - self.storage = anaconda.id.storage - self.vg = vg + self.storage = anaconda.id.storage + + # the vg instance we were passed + self.vg = vg + self.peSize = vg.peSize + self.pvs = self.vg.pvs[:] + + # a dict of dicts + # keys are lv names + # values are dicts representing the lvs + # name, size, format instance, exists self.lvs = {} - self.isNew = isNew - self.intf = intf - self.parent = parent + + # a dict of luks devices + # keys are lv names + # values are formats of the mapped devices + self.luks = {} + + self.isNew = isNew + self.intf = intf + self.parent = parent self.actions = [] for lv in self.vg.lvs: - self.lvs[lv.lvname] = lv + self.lvs[lv.lvname] = {"name": lv.lvname, + "size": lv.size, + "format": copy.copy(lv.format), + "exists": lv.exists} + + if lv.format.type == "luks": + try: + self.luks[lv.lvname] = self.storage.devicetree.getChildren(lv)[0].format + except IndexError: + self.luks[lv.lvname] = lv.format self.availlvmparts = self.storage.unusedPVs(vg=vg) @@ -997,7 +1152,7 @@ class VolumeGroupEditor: maintable.attach(self.peCombo, 1, 2, row, row + 1, gtk.EXPAND|gtk.FILL, gtk.SHRINK) row = row + 1 - (self.lvmlist, sw) = self.createAllowedLvmPartitionsList(self.availlvmparts, self.storage.vgs) + (self.lvmlist, sw) = self.createAllowedLvmPartitionsList() if vg.exists: self.lvmlist.set_sensitive(False) self.lvmlist.set_size_request(275, 80) @@ -1065,11 +1220,9 @@ class VolumeGroupEditor: iter = self.logvolstore.append() self.logvolstore.set_value(iter, 0, lv.lvname) if lv.format.type == "luks": - usedev = self.storage.devicetree.getChildren(lv)[0] - format = usedev.format + format = self.storage.devicetree.getChildren(lv)[0].format else: - usedev = lv - format = usedev.format + format = lv.format if getattr(format, "mountpoint", None): self.logvolstore.set_value(iter, 1, -- cgit From d90a14bb68d03085cc71780e25d712296a685a89 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 19 Mar 2009 04:15:02 -0500 Subject: New version: 11.5.0.33 --- anaconda.spec | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index 48e1564cb..324c5d4bb 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.32 +Version: 11.5.0.33 Release: 1 License: GPLv2+ Group: Applications/System @@ -210,6 +210,47 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Thu Mar 19 2009 David Lehman - 11.5.0.33-1 +- Rework the lvm dialog. (#490301,#490966,#490681,#489870) (dlehman) +- Improve chances of uniqueness from Storage.createSuggestedLVName. (dlehman) +- Fix pruning of destroy actions for preexisting devices. (dlehman) +- Devices should not be resizable unless they exist. (dlehman) +- Try to activate an existing md array after adding each member. (dlehman) +- Indicate filesystem is mountable if we have a mount command. (dcantrell) +- Mount existing filesystems read-only when getting size. (dcantrell) +- Fix some errors in the updates target. (dcantrell) +- Place all mount.* commands in /sbin (dcantrell) +- Fix error message reading and writing in doPwMount() (dcantrell) +- Use booleans in isys.mount() and isys.umount() (dcantrell) +- Add a FIXME comment for setting uuid in VG / LV create (hdegoede) +- Do not traceback when writing anaconda.ks with iscsi with auth info. + (hdegoede) +- Do not write LV uuid to grub.conf, but the filesystem uuid (hdegoede) +- If a mountpoint depends on a network disk at _netdev to its fstab options + (hdegoede) +- Do not hang when creating raid array with member having filesystem + detected (#490891) (rvykydal) +- Destroy and create luks child of raid array too when editing in UI. + (rvykydal) +- Editing non-existent raid device by destroying and creating actions + (rvykydal) +- actionDestroyFormat call takes device, not format (rvykydal) +- Fix getChildren call in partition UI (rvykydal) +- Fix removing of devices with the same name from tree when adding + create action. (rvykydal) +- Do not duplicate requested minor number in edit raid UI list. (rvykydal) +- Offer available partitions when editing non-preexisting raid request. + (rvykydal) +- Don't try to fit the whole StorageDevice.__str__ output into the UI + (#490406). (clumens) +- Make PartitionDevice handle both normal and dmraid partitions (hdegoede) +- Stop overriding __init__ in DMRaidPartitionDevice (hdegoede) +- Set format UUID after creating a format (hdegoede) +- Fix result of updateSysfsPath to be consistent with initial sysfsPath + values (hdegoede) +- Use getDevicesByInstance() for storage.partitions (hdegoede) +- We no longer use iscsiadm anywhere (hdegoede) + * Tue Mar 17 2009 Jesse Keating - 11.5.0.32-1 - Typo fix. (clumens) - Make platform.checkBootRequest work better and not use diskset anymore. (clumens) -- cgit From cfd858e8b36a6beef6b5f2da02d78c047128b880 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Thu, 19 Mar 2009 14:18:24 +0100 Subject: Fix New partition in UI Pass format to new partition (request) so that it doesn't have to create DeviceFormat instance which tracebacks with: http://fpaste.org/paste/6475 --- iw/partition_dialog_gui.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index 5abbd512c..45d940958 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -159,16 +159,17 @@ class PartitionEditor: if disk.name == drive: disks.append(disk) + format = fmt_class(mountpoint=mountpoint) if self.isNew: request = self.storage.newPartition(size=size, grow=grow, maxsize=maxsize, primary=primary, + format=format, parents=disks) else: request = self.origrequest - format = fmt_class(mountpoint=mountpoint) if self.lukscb and self.lukscb.get_active() and \ request.format.type != "luks": luksformat = format -- cgit From 62d16b5ebd79aa478af1298b7176d463a2cc5f31 Mon Sep 17 00:00:00 2001 From: Jeremy Katz Date: Thu, 19 Mar 2009 11:35:22 -0400 Subject: Fix live installs to not traceback Fix traceback at end of live installs for the storage branch --- livecd.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/livecd.py b/livecd.py index 2d09717aa..d05dc7950 100644 --- a/livecd.py +++ b/livecd.py @@ -341,7 +341,7 @@ class LiveCDCopyBackend(backend.AnacondaBackend): # now write out the "real" fstab and mtab anaconda.id.storage.write(anaconda.rootPath) f = open(anaconda.rootPath + "/etc/mtab", "w+") - f.write(anaconda.id.fsset.mtab()) + f.write(anaconda.id.storage.fsset.mtab()) f.close() # copy over the modprobe.conf -- cgit From 3264da56554cebba460f18efbb2e01231b5b9d46 Mon Sep 17 00:00:00 2001 From: Jeremy Katz Date: Thu, 19 Mar 2009 11:39:05 -0400 Subject: try to unmount everything from /media on live installs devicekit-disks is mounting fixed disks right now which then causes us pain in the installer. let's try to unmount everything before anaconda starts --- liveinst/liveinst.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/liveinst/liveinst.sh b/liveinst/liveinst.sh index cc6e3d5f8..744a2b9bd 100644 --- a/liveinst/liveinst.sh +++ b/liveinst/liveinst.sh @@ -56,6 +56,8 @@ if [ ! -e /selinux/load ]; then ANACONDA="$ANACONDA --noselinux" fi +# devkit-disks is now mounting lots of stuff. for now, let's just try to unmount it all +umount /media/* /sbin/swapoff -a /sbin/lvm vgchange -an --ignorelockingfailure -- cgit From 582dfa9e781a172765b5c2f9ce770a41bac77e68 Mon Sep 17 00:00:00 2001 From: Jeremy Katz Date: Thu, 19 Mar 2009 12:55:06 -0400 Subject: Inhibit devkit-disks during a live install This is a bit of a big hammer, but disable devkit-disks from touching things during a live install until we can do something a little bit more fine-grained --- liveinst/liveinst.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/liveinst/liveinst.sh b/liveinst/liveinst.sh index 744a2b9bd..cdba58f03 100644 --- a/liveinst/liveinst.sh +++ b/liveinst/liveinst.sh @@ -61,7 +61,9 @@ umount /media/* /sbin/swapoff -a /sbin/lvm vgchange -an --ignorelockingfailure -if [ -x /usr/bin/hal-lock -a -e /var/lock/subsys/haldaemon ]; then +if [ -x /usr/bin/devkit-disks ]; then + /usr/bin/devkit-disks --inhibit -- /usr/bin/hal-lock --interface org.freedesktop.Hal.Device.Storage --exclusive --run "$ANACONDA $*" +elif [ -x /usr/bin/hal-lock -a -e /var/lock/subsys/haldaemon ]; then /usr/bin/hal-lock --interface org.freedesktop.Hal.Device.Storage --exclusive --run "$ANACONDA $*" else $ANACONDA $* -- cgit From 8e9e908f53c6c1124cfda9acb70c8d3e465e9fd8 Mon Sep 17 00:00:00 2001 From: Jeremy Katz Date: Thu, 19 Mar 2009 13:57:14 -0400 Subject: Move blockdev blacklisting to be a function --- storage/udev.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/storage/udev.py b/storage/udev.py index a6d3190cb..e8e824698 100644 --- a/storage/udev.py +++ b/storage/udev.py @@ -39,11 +39,18 @@ def udev_get_block_devices(): entries.append(entry) return entries +def __is_blacklisted_blockdev(dev_name): + """Is this a blockdev we never want for an install?""" + if dev_name.startswith("loop") or dev_name.startswith("ram"): + return True + + return False + def enumerate_block_devices(): top_dir = "/sys/class/block" devices = [] for dev_name in os.listdir(top_dir): - if dev_name.startswith("loop") or dev_name.startswith("ram"): + if __is_blacklisted_blockdev(dev_name): continue full_path = os.path.join(top_dir, dev_name) link_ref = os.readlink(full_path) -- cgit From f03d7e9fd4d3f346f2d0b17fc7713fc886b2ca94 Mon Sep 17 00:00:00 2001 From: Jeremy Katz Date: Thu, 19 Mar 2009 13:57:40 -0400 Subject: Blacklist the live image backing device We can't let the user install to the usb key holding their live image without causing significant trouble. So blacklist that dev --- storage/udev.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/storage/udev.py b/storage/udev.py index e8e824698..38946482c 100644 --- a/storage/udev.py +++ b/storage/udev.py @@ -22,6 +22,7 @@ import os import re +import stat import iutil from errors import * @@ -43,6 +44,15 @@ def __is_blacklisted_blockdev(dev_name): """Is this a blockdev we never want for an install?""" if dev_name.startswith("loop") or dev_name.startswith("ram"): return True + # FIXME: the backing dev for the live image can't be used as an + # install target. note that this is a little bit of a hack + # since we're assuming that /dev/live will exist + if os.path.exists("/dev/live") and \ + stat.S_ISBLK(os.stat("/dev/live")[stat.ST_MODE]): + livetarget = os.path.realpath("/dev/live") + if livetarget.startswith(dev_name): + log.info("%s looks to be the live device; ignoring" % (dev_name,)) + return True return False -- cgit From ead6f49dbca873750f577917e820f439d383e317 Mon Sep 17 00:00:00 2001 From: Jeremy Katz Date: Thu, 19 Mar 2009 14:00:44 -0400 Subject: Add more blacklisting We used to also blacklist IBM STMF (iSeries kernel storage), SCEI Flash (PS3 flash memory) and DGC LUNZ (luns we don't have access to) so we should continue to do so to avoid trying to install to things we know will cause failures --- storage/udev.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/storage/udev.py b/storage/udev.py index 38946482c..fcff5af65 100644 --- a/storage/udev.py +++ b/storage/udev.py @@ -54,6 +54,13 @@ def __is_blacklisted_blockdev(dev_name): log.info("%s looks to be the live device; ignoring" % (dev_name,)) return True + if os.path.exists("/sys/class/block/%s/device/model" %(dev_name,)): + model = open("/sys/class/block/%s/device/model" %(dev_name,)).read() + for bad in ("IBM *STMF KERNEL", "SCEI Flash-5", "DGC LUNZ"): + if model.find(bad) != -1: + log.info("ignoring %s with model %s" %(dev_name, model)) + return True + return False def enumerate_block_devices(): -- cgit From 22ff5bd2d0e4f5b247c733c75ba466f9e99e8cad Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 19 Mar 2009 15:36:20 -0500 Subject: Revert "Fix pruning of destroy actions for preexisting devices." This reverts commit 9abfdc92a55618e6b2994c19fede8316405fb832. This is the wrong fix, if one was even really needed in the first place. --- storage/devicetree.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index 2b997b58e..f7582aced 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -223,11 +223,6 @@ class DeviceTree(object): # this device is not preexisting start = first_create_idx stop_action = destroys[-1] - else: - # no create actions means this is a preexisting device, - # so we prune all actions up through the last destroy. - start = 0 - stop_action = destroys[-1] if start is None: continue -- cgit From bfc10b4c4432ac564379d1771bbbda64ac0400ab Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Thu, 19 Mar 2009 15:00:27 -0400 Subject: Fix writing the default= line in grub.conf (#490756). This was caused by trying to compare an instance of an object with a string, which of course is never going to work. --- bootloader.py | 11 ++++++----- booty/bootloaderInfo.py | 2 +- booty/s390.py | 2 +- booty/x86.py | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/bootloader.py b/bootloader.py index f9209528c..08e874ce3 100644 --- a/bootloader.py +++ b/bootloader.py @@ -136,17 +136,18 @@ def writeBootloader(anaconda): kernelList = [] otherList = [] - rootDev = getattr(anaconda.id.storage.fsset.rootDevice, "name", None) - defaultDev = anaconda.id.bootloader.images.getDefault() + # getDefault needs to return a device, but that's too invasive for now. + rootDev = anaconda.id.storage.fsset.rootDevice + defaultDev = anaconda.id.storage.devicetree.getDeviceByName(anaconda.id.bootloader.images.getDefault()) kernelLabel = None kernelLongLabel = None for (dev, (label, longlabel, type)) in anaconda.id.bootloader.images.getImages().items(): - if (dev == rootDev) or (rootDev is None and kernelLabel is None): + if (rootDev is None and kernelLabel is None) or (dev == rootDev.name): kernelLabel = label kernelLongLabel = longlabel - elif dev == defaultDev: + elif dev == defaultDev.name: otherList = [(label, longlabel, dev)] + otherList else: otherList.append((label, longlabel, dev)) @@ -175,7 +176,7 @@ def writeBootloader(anaconda): f.write("# UPDATEDEFAULT specifies if new-kernel-pkg should make\n" "# new kernels the default\n") # only update the default if we're setting the default to linux (#156678) - if rootDev == defaultDev: + if rootDev.name == defaultDev.name: f.write("UPDATEDEFAULT=yes\n") else: f.write("UPDATEDEFAULT=no\n") diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 90073e279..8419645fa 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -333,7 +333,7 @@ class bootloaderInfo: rootDev = self.storage.fsset.rootDevice - if rootDev == defaultDev: + if rootDev.name == defaultDev.name: lilo.addEntry("default", kernelList[0][0]) else: lilo.addEntry("default", chainList[0][0]) diff --git a/booty/s390.py b/booty/s390.py index 655d028b8..8cddb053d 100644 --- a/booty/s390.py +++ b/booty/s390.py @@ -27,7 +27,7 @@ class s390BootloaderInfo(bootloaderInfo): rootDev = self.storage.fsset.rootDevice - if rootDev == defaultDev: + if rootDev.name == defaultDev.name: lilo.addEntry("default", kernelList[0][0]) else: lilo.addEntry("default", chainList[0][0]) diff --git a/booty/x86.py b/booty/x86.py index 05c9feb75..221186a41 100644 --- a/booty/x86.py +++ b/booty/x86.py @@ -184,7 +184,7 @@ class x86BootloaderInfo(efiBootloaderInfo): # get the default image to boot... we have to walk and find it # since grub indexes by where it is in the config file - if defaultDev == rootDev: + if defaultDev.name == rootDev.name: default = 0 else: # if the default isn't linux, it's the first thing in the -- cgit From cc2387dc13ce3d98b90b62db2ed1a3a92d28a898 Mon Sep 17 00:00:00 2001 From: Jeremy Katz Date: Thu, 19 Mar 2009 16:51:27 -0400 Subject: Let's not remove our mountpoints isys.umount() defaults to removing directories. Maybe we should change that, but it has implications for a fair number of callers. So let's just not remove the mountpoints for now --- storage/formats/fs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 71160bb7c..c624c592d 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -508,7 +508,7 @@ class FS(DeviceFormat): if not os.path.exists(self._mountpoint): raise FSError("mountpoint does not exist") - rc = isys.umount(self._mountpoint) + rc = isys.umount(self._mountpoint, removeDir = False) if rc: raise FSError("umount failed") -- cgit From 33cdffbb842d0bc2e721e96b8c6818d535ea758e Mon Sep 17 00:00:00 2001 From: Jeremy Katz Date: Thu, 19 Mar 2009 17:13:39 -0400 Subject: Fix up checking for live image backing /dev/live is an absolute symlink right now, so strip off /dev if we need to --- storage/udev.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/storage/udev.py b/storage/udev.py index fcff5af65..11df62927 100644 --- a/storage/udev.py +++ b/storage/udev.py @@ -50,6 +50,8 @@ def __is_blacklisted_blockdev(dev_name): if os.path.exists("/dev/live") and \ stat.S_ISBLK(os.stat("/dev/live")[stat.ST_MODE]): livetarget = os.path.realpath("/dev/live") + if livetarget.startswith("/dev"): + livetarget = livetarget[5:] if livetarget.startswith(dev_name): log.info("%s looks to be the live device; ignoring" % (dev_name,)) return True -- cgit From 5f217a04e0bbd1fa749b66bda7837270a17bf33a Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Mon, 16 Mar 2009 13:24:20 +0100 Subject: Handle system crappyness. * storage/__init__.py (createSuggestedVGName): Take into account the new lvm black list. * storage/devicelibs/lvm.py (blacklistVG): New function, add a VG to a the black list. * storage/devicelibs/lvm.py (vgreduce): introduces a new argument to the function. rm means use the lvm --removemissing option. * storage/devices.py (LVMVolumeGroupDevice.complete): New function to evaluate if VG is consistent. * storage/devices.py (LVMLogicalVolumeDevice.complete): Likewise for LVs. * storage/devicetree.py (_handleInconsistencies): New function intended to catch all unwanted behavior from the system before continuing. * storage/devicetree.py (questionReinitILVM): New function intended to ask the user what to do in case we find inconsistent LVM metadata. --- iw/partition_gui.py | 2 +- storage/__init__.py | 6 ++- storage/devicelibs/lvm.py | 27 +++++++--- storage/devices.py | 24 +++++++-- storage/devicetree.py | 124 +++++++++++++++++++++++++++++++++++++++++++- storage/formats/__init__.py | 7 ++- storage/formats/lvmpv.py | 6 ++- 7 files changed, 179 insertions(+), 17 deletions(-) diff --git a/iw/partition_gui.py b/iw/partition_gui.py index 0b52a70fb..6ce1b11a1 100644 --- a/iw/partition_gui.py +++ b/iw/partition_gui.py @@ -819,7 +819,7 @@ class PartitionWindow(InstallWindow): partName = part.getDeviceNodeName().split("/")[-1] device = self.storage.devicetree.getDeviceByName(partName) if not device and not part.type & parted.PARTITION_FREESPACE: - raise RuntimeError("can't find partition %s in device" + log.debug("can't find partition %s in device" " tree" % partName) # ignore the tiny < 1 MB partitions (#119479) diff --git a/storage/__init__.py b/storage/__init__.py index 7b7ab5c68..6f181d3de 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -632,13 +632,15 @@ class Storage(object): else: vgtemplate = "VolGroup" - if vgtemplate not in vgnames: + if vgtemplate not in vgnames and \ + vgtemplate not in lvm.lvm_vg_blacklist: return vgtemplate else: i = 0 while 1: tmpname = "%s%02d" % (vgtemplate, i,) - if not tmpname in vgnames: + if not tmpname in vgnames and \ + tmpname not in lvm.lvm_vg_blacklist: break i += 1 diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py index 8b755a4e0..aa930718b 100644 --- a/storage/devicelibs/lvm.py +++ b/storage/devicelibs/lvm.py @@ -73,7 +73,7 @@ def _composeConfig(): if len(rejects) > 0: for i in range(len(rejects)): - filter_string = filter_string + ("\"r|%s|\", " % rejects[i]) + filter_string = filter_string + ("\"r|%s|\"," % rejects[i]) filter_string = " filter=[%s] " % filter_string.strip(",") @@ -86,7 +86,7 @@ def _composeConfig(): # devices_string can have (inside the brackets) "dir", "scan", # "preferred_names", "filter", "cache_dir", "write_cache_state", # "types", "sysfs_scan", "md_component_detection". see man lvm.conf. - devices_string = " devices { %s } " % (filter_string) # strings can be added + devices_string = " devices {%s} " % (filter_string) # strings can be added config_string = devices_string # more strings can be added. config_args = ["--config", config_string] @@ -99,6 +99,12 @@ def lvm_cc_addFilterRejectRegexp(regexp): _composeConfig() # End config_args handling code. +# Names that should not be used int the creation of VGs +lvm_vg_blacklist = [] +def blacklistVG(name): + global lvm_vg_blacklist + lvm_vg_blacklist.append(name) + 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. @@ -283,11 +289,18 @@ def vgdeactivate(vg_name): if rc: raise LVMError("vgdeactivate failed for %s" % vg_name) -def vgreduce(vg_name, pv_list): - args = ["vgreduce"] + \ - config_args + \ - [vg_name] + \ - pv_list +def vgreduce(vg_name, pv_list, rm=False): + """ Reduce a VG. + + rm -> with RemoveMissing option. + Use pv_list when rm=False, otherwise ignore pv_list and call vgreduce with + the --removemissing option. + """ + args = ["vgreduce"] + if rm: + args.extend(["--removemissing", vg_name]) + else: + args.extend([vg_name] + pv_list) rc = iutil.execWithRedirect("lvm", args, stdout = "/dev/tty5", diff --git a/storage/devices.py b/storage/devices.py index 1a52c7119..62bc8041d 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -1633,9 +1633,15 @@ class LVMVolumeGroupDevice(DMDevice): self.teardown() - lvm.vgremove(self.name) - self.notifyKernel() - self.exists = False + # this sometimes fails for some reason. + try: + lvm.vgreduce(self.name, [], rm=True) + lvm.vgremove(self.name) + except lvm.LVMErorr: + raise DeviceError("Could not completely remove VG %s" % self.name) + finally: + self.notifyKernel() + self.exists = False def reduce(self, pv_list): """ Remove the listed PVs from the VG. """ @@ -1764,6 +1770,13 @@ class LVMVolumeGroupDevice(DMDevice): """ A list of this VG's LVs """ return self._lvs[:] # we don't want folks changing our list + @property + def complete(self): + """Check if the vg has all its pvs in the system + Return True if complete. + """ + return len(self.pvs) == self.pvCount or not self.exists + class LVMLogicalVolumeDevice(DMDevice): """ An LVM Logical Volume """ @@ -1863,6 +1876,11 @@ class LVMLogicalVolumeDevice(DMDevice): """ The LV's name (not including VG name). """ return self._name + @property + def complete(self): + """ Test if vg exits and if it has all pvs. """ + return self.vg.complete + def setup(self, intf=None): """ Open, or set up, a device. """ log_method_call(self, self.name, status=self.status) diff --git a/storage/devicetree.py b/storage/devicetree.py index f7582aced..ae6bed9b4 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -136,6 +136,36 @@ def questionInitializeDisk(intf=None, name=None): retVal = True return retVal +def questionReinitILVM(intf=None, pv_names=None, lv_name=None, vg_name=None): + retVal = False # The less destructive default + if not intf or not pv_names or (lv_name is None and vg_name is None): + pass + else: + if vg_name is not None: + message = "%s Volume Group" % vg_name + elif lv_name is not None: + message = "%s Logical Volume" % lv_name + + + rc = intf.messageWindow(_("Warning"), + _("Error processing LVM.\n" + "It seems that there is inconsistent LVM data. " + "(%s) make(s) up %s. " + "You can reinitialize all related PVs, which will " + "erase all LVM metadata. Or ignore, which will " + "preserve contents.") + %(str(pv_names), message), + type="custom", + custom_buttons = [ _("_Ignore drive(s)"), + _("_Re-initialize drive(s)") ], + custom_icon="question") + if rc == 0: + pass + else: + retVal = True # thie means clobber. + + return retVal + class DeviceTree(object): """ A quasi-tree that represents the devices in the system. @@ -647,7 +677,7 @@ class DeviceTree(object): log.debug("added %s (%s) to device tree" % (newdev.name, newdev.type)) - def _removeDevice(self, dev, force=None): + def _removeDevice(self, dev, force=None, moddisk=True): """ Remove a device from the tree. Only leaves may be removed. @@ -660,7 +690,8 @@ class DeviceTree(object): raise ValueError("Cannot remove non-leaf device '%s'" % dev.name) # if this is a partition we need to remove it from the parted.Disk - if isinstance(dev, PartitionDevice) and dev.disk is not None: + if moddisk and isinstance(dev, PartitionDevice) and \ + dev.disk is not None: # if this partition hasn't been allocated it could not have # a disk attribute dev.disk.partedDisk.removePartition(dev.partedPartition) @@ -1323,12 +1354,96 @@ class DeviceTree(object): size=lv_size, exists=True) self._addDevice(lv_device) + try: lv_device.setup() except DeviceError as e: log.info("setup of %s failed: %s" % (lv_device.name, e)) + def _handleInconsistencies(self, device): + def reinitializeVG(vg): + # First we remove VG data + try: + vg.destroy() + except DeviceError: + # the pvremoves will finish the job. + log.debug("There was an error destroying the VG %s." % vg.name) + pass + + # remove VG device from list. + self._removeDevice(vg) + + for parent in vg.parents: + parent.format.destroy() + + # Give the vg the a default format + kwargs = {"uuid": parent.uuid, + "label": parent.diskLabel, + "device": parent.path, + "exists": parent.exists} + parent.format = formats.getFormat(*[""], **kwargs) + + if device.type == "lvmvg": + paths = [] + for parent in device.parents: + paths.append(parent.path) + + # when zeroMbr is true he wont ask. + if not device.complete and (self.zeroMbr or \ + questionReinitILVM(intf=self.intf, \ + vg_name=device.name, pv_names=paths)): + reinitializeVG(device) + + elif not device.complete: + # The user chose not to reinitialize. + # hopefully this will ignore the vg components too. + self._removeDevice(vg) + lvm.lvm_cc_addFilterRejectRegexp(vg.name) + lvm.blacklistVG(vg.name) + for parent in vg.parents: + self._removeDevice(parent, moddisk=False) + lvm.lvm_cc_addFilterRejectRegexp(parent.name) + + return + + elif device.type == "lvmlv": + # we might have already fixed this. + if device not in self._devices or \ + device.name in self._ignoredDisks: + return + + paths = [] + for parent in device.vg.parents: + paths.append(parent.path) + + if not device.complete and (self.zeroMbr or \ + questionReinitILVM(intf=self.intf, \ + lv_name=device.name, pv_names=paths)): + + # destroy all lvs. + for lv in device.vg.lvs: + lv.destroy() + device.vg._removeLogVol(lv) + self._removeDevice(lv) + + reinitializeVG(device.vg) + + elif not device.complete: + # ignore all the lvs. + for lv in device.vg.lvs: + self._removeDevice(lv) + lvm.lvm_cc_addFilterRejectRegexp(lv.name) + # ignore the vg + self._removeDevice(device.vg) + lvm.lvm_cc_addFilterRejectRegexp(device.vg.name) + lvm.blacklistVG(device.vg.name) + # ignore all the pvs + for parent in device.vg.parents: + self._removeDevice(parent, moddisk=False) + lvm.lvm_cc_addFilterRejectRegexp(parent.name) + return + def populate(self): """ Locate all storage devices. """ # each iteration scans any devices that have appeared since the @@ -1358,6 +1473,11 @@ class DeviceTree(object): for dev in devices: self.addUdevDevice(dev) + # After having the complete tree we make sure that the system + # inconsistencies are ignored or resolved. + for leaf in self.leaves: + self._handleInconsistencies(leaf) + self.teardownAll() def teardownAll(self): diff --git a/storage/formats/__init__.py b/storage/formats/__init__.py index 3eaeb0fd7..9ec099932 100644 --- a/storage/formats/__init__.py +++ b/storage/formats/__init__.py @@ -253,7 +253,12 @@ class DeviceFormat(object): os.lseek(fd, -1024 * 1024, 2) os.write(fd, buf) os.close(fd) - except Exception, e: + except OSError as e: + if getattr(e, "errno", None) == 28: # No space left in device + pass + else: + log.error("error zeroing out %s: %s" % (self.device, e)) + except Exception as e: log.error("error zeroing out %s: %s" % (self.device, e)) self.exists = False diff --git a/storage/formats/lvmpv.py b/storage/formats/lvmpv.py index cc243ebfe..c9cbe295d 100644 --- a/storage/formats/lvmpv.py +++ b/storage/formats/lvmpv.py @@ -106,7 +106,11 @@ class LVMPhysicalVolume(DeviceFormat): raise PhysicalVolumeError("device is active") # FIXME: verify path exists? - lvm.pvremove(self.device) + try: + lvm.pvremove(self.device) + except LVMError: + DeviceFormat.destroy(self, *args, **kwargs) + self.exists = False self.notifyKernel() -- cgit From fea3b2ea04662f87fa4c6d1325be8d062afd4d4f Mon Sep 17 00:00:00 2001 From: Jeremy Katz Date: Thu, 19 Mar 2009 19:00:22 -0400 Subject: After setting up our random UUID, inform the storage layer The storage layer keeps track of uuids for filesystems, so we need to update it if we change the uuid --- livecd.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/livecd.py b/livecd.py index d05dc7950..96610cd1b 100644 --- a/livecd.py +++ b/livecd.py @@ -29,6 +29,7 @@ import stat import shutil import time import subprocess +import storage import selinux @@ -238,6 +239,13 @@ class LiveCDCopyBackend(backend.AnacondaBackend): stdout="/dev/tty5", stderr="/dev/tty5", searchPath = 1) + # and now set the uuid in the storage layer + storage.udev.udev_settle() + rootDevice.updateSysfsPath() + iutil.notify_kernel("/sys%s" %rootDevice.sysfsPath) + info = storage.udev.udev_get_block_device("/sys%s" % rootDevice.sysfsPath) + rootDevice.format.uuid = storage.udev.udev_device_get_uuid(info) + log.info("reset the rootdev (%s) to have a uuid of %s" %(rootDevice.sysfsPath, rootDevice.format.uuid)) # for any filesystem that's _not_ on the root, we need to handle # moving the bits from the livecd -> the real filesystems. -- cgit From 22c388145a0620b5748de13cd67a0af12c19ae53 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 19 Mar 2009 22:13:42 -0500 Subject: Make bootable a property of PartitionDevice. --- storage/devices.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 62bc8041d..edebeba96 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -861,7 +861,7 @@ class PartitionDevice(StorageDevice): self.req_base_size = 0 self.req_max_size = 0 - self.bootable = False + self._bootable = False self._resize = False @@ -1043,23 +1043,26 @@ class PartitionDevice(StorageDevice): log_method_call(self, self.name) StorageDevice._setFormat(self, format) - def setBootable(self, bootable): + def _setBootable(self, bootable): """ Set the bootable flag for this partition. """ if self.partedPartition: - if isFlagAvailable(parted.PARTITION_BOOT): + if self.flagAvailable(parted.PARTITION_BOOT): if bootable: - self.partedPartition.setFlag(parted.PARTITION_BOOT) + self.setFlag(parted.PARTITION_BOOT) else: - self.partedPartition.unsetFlag(parted.PARTITION_BOOT) + self.unsetFlag(parted.PARTITION_BOOT) else: raise DeviceError(_("boot flag not available for this " "partition")) + + self._bootable = bootable else: - if self.partType != parted.PARTITION_NORMAL: - raise DeviceError(_("boot flag only available to primary " - "partitions")) - else: - self.bootable = bootable + self.req_bootable = bootable + + def _getBootable(self): + return self._bootable or self.req_bootable + + bootable = property(_getBootable, _setBootable) def flagAvailable(self, flag): log_method_call(self, path=self.path, flag=flag, @@ -1108,6 +1111,8 @@ class PartitionDevice(StorageDevice): self._partType = self.partedPartition.type + self._bootable = self.getFlag(parted.PARTITION_BOOT) + def create(self, intf=None): """ Create the device. """ log_method_call(self, self.name, status=self.status) -- cgit From 08924fe8952b93962eeb7b4f2ee2d999b24da71a Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 19 Mar 2009 22:15:15 -0500 Subject: Make sure boot flag gets set. (#491170) --- storage/__init__.py | 10 ++++++++++ storage/deviceaction.py | 4 ---- storage/partitioning.py | 3 ++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 6f181d3de..c5d3e4718 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -183,6 +183,16 @@ class Storage(object): def doIt(self): self.devicetree.processActions() + # now set the boot partition's flag + try: + boot = self.anaconda.platform.bootDevice() + except DeviceError: + boot = None + else: + if hasattr(boot, "bootable"): + boot.bootable = True + boot.disk.commit() + @property def nextID(self): id = self._nextID diff --git a/storage/deviceaction.py b/storage/deviceaction.py index 3f7ce916d..ba4456a6e 100644 --- a/storage/deviceaction.py +++ b/storage/deviceaction.py @@ -269,10 +269,6 @@ class ActionCreateFormat(DeviceAction): self.device.setFlag(self.format.partedFlag) self.device.disk.commit() - if self.device.bootable: - self.device.setFlag(PARTITION_BOOT) - self.device.disk.commit() - udev_settle() self.device.setup() self.device.format.create(intf=intf, diff --git a/storage/partitioning.py b/storage/partitioning.py index 92458d94f..fdaf36021 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -538,6 +538,7 @@ def doPartitioning(storage, exclusiveDisks=None): partitions = storage.partitions for part in partitions: + part.req_bootable = False if not part.exists: # start over with flexible-size requests part.req_size = part.req_base_size @@ -548,7 +549,7 @@ def doPartitioning(storage, exclusiveDisks=None): except DeviceError: bootDev = None - if bootDev and not bootDev.exists: + if bootDev: bootDev.req_bootable = True # FIXME: make sure non-existent partitions have empty parents list -- cgit From 91347c23b46687114b1d7a4f02867129170bba84 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Thu, 19 Mar 2009 22:17:16 -0500 Subject: Add boot partition size limit properties and size validation method. Also, don't require that partitions be bootable in advance of being identified as the boot device. We will set the boot device's boot flag explicity elsewhere. --- platform.py | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/platform.py b/platform.py index 71db883a8..330601adb 100644 --- a/platform.py +++ b/platform.py @@ -41,6 +41,8 @@ class Platform(object): _diskType = parted.diskType["msdos"] _minimumSector = 0 _supportsMdRaidBoot = False + _minBootPartSize = 50 + _maxBootPartSize = 0 def __init__(self, anaconda): """Creates a new Platform object. This is basically an abstract class. @@ -127,8 +129,27 @@ class Platform(object): """Does the platform support /boot on MD RAID?""" return self._supportsMdRaidBoot + @property + def minBootPartSize(self): + return self._minBootPartSize + + @property + def maxBootPartSize(self): + return self._maxBootPartSize + + def validBootPartSize(self, size): + """ Is the given size (in MB) acceptable for a boot device? """ + if not isinstance(size, int) and not isinstance(size, float): + return False + + return ((not self.minBootPartSize or size >= self.minBootPartSize) + and + (not self.maxBootPartSize or size <= self.maxBootPartSize)) + class EFI(Platform): _diskType = parted.diskType["gpt"] + _minBootPartSize = 50 + _maxBootPartSize = 256 def bootDevice(self): mntDict = self._mntDict() @@ -157,7 +178,7 @@ class EFI(Platform): # Only add the EFI partition to the default set if there's not already # one on the system. - if len(filter(lambda dev: dev.format.type == "efi" and dev.size < 256 and dev.bootable, + if len(filter(lambda dev: dev.format.type == "efi" and self.validBootPartSize(dev.size), self.anaconda.id.storage.partitions)) == 0: ret.append(("/boot/efi", "efi", 50, 200, 1, 0)) @@ -207,6 +228,9 @@ class PPC(Platform): return self._ppcMachine class IPSeriesPPC(PPC): + _minBootPartSize = 4 + _maxBootPartSize = 10 + def bootDevice(self): bootDev = None @@ -248,13 +272,15 @@ class IPSeriesPPC(PPC): class NewWorldPPC(PPC): _diskType = parted.diskType["mac"] + _minBootPartSize = (800.00 / 1024.00) + _maxBootPartSize = 1 def bootDevice(self): bootDev = None for part in self.anaconda.id.storage.partitions: # XXX do we need to also check the size? - if part.format.type == "hfs" and part.bootable: + if part.format.type == "hfs" and self.validBootPartSize(part.size): bootDev = part return bootDev @@ -340,6 +366,20 @@ class X86(EFI): def isEfi(self): return self._isEfi + @property + def maxBootPartSize(self): + if self.isEfi: + return EFI._maxBootPartSize + else: + return Platform._maxBootPartSize + + @property + def minBootPartSize(self): + if self.isEFI: + return EFI._minBootPartSize + else: + return Platform._minBootPartSize + def setDefaultPartitioning(self): if self.isEfi: return EFI.setDefaultPartitioning(self) -- cgit From 44910a2fd019b1a0b06166ceaadc556e05ed2971 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Wed, 18 Mar 2009 17:48:16 -1000 Subject: Attempt disk commits 5 times before raising an exception. Hitting this periodically on my test systems. The disk may or may not be capable of the ped_disk_commit_to_dev() call yet. The disk is busy. I don't want to use a Timer thread since we're using PyGTK and we'll get in to all sorts of problems if threads are brought it. The poor man's try loop has been working for me today. --- storage/devices.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/storage/devices.py b/storage/devices.py index edebeba96..e1f08d378 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -782,7 +782,22 @@ class DiskDevice(StorageDevice): self.setupParents() self.setup() - self.partedDisk.commit() + + # give committing 5 tries, failing that, raise an exception + attempt = 1 + maxTries = 5 + keepTrying = True + + while keepTrying and (attempt <= maxTries): + try: + self.partedDisk.commit() + keepTrying = False + except parted.DiskException as msg: + log.warning(msg) + attempt += 1 + + if keepTrying: + raise DeviceError("cannot commit to disk %s after %d attempts" % (self.name, maxTries,)) def destroy(self): """ Destroy the device. """ -- cgit From 6e15d6c6f3bdb5750bb08c6db770c4bfc5eebbaf Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 19 Mar 2009 11:05:51 -1000 Subject: Avoid SIGSEGV in doPwMount() when NULL is last parameter (#491192) If NULL is given as the last parameter to doPwMount(), do not try to dereference it. --- isys/imount.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/isys/imount.c b/isys/imount.c index 614e67ee6..fd51fdefa 100644 --- a/isys/imount.c +++ b/isys/imount.c @@ -146,9 +146,11 @@ int doPwMount(char *dev, char *where, char *fs, char *options, char **err) { close(pipefd[1]); - if (*err != NULL) { - rc = readFD(pipefd[0], err); - rc = write(programLogFD, *err, 4096); + if (err != NULL) { + if (*err != NULL) { + rc = readFD(pipefd[0], err); + rc = write(programLogFD, *err, 4096); + } } close(pipefd[0]); -- cgit From 8683176aef29562f6e7505a7727b79f94e1c8a8c Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 19 Mar 2009 11:38:45 -1000 Subject: mount and umount commands are in /sbin now, remove from /usr/sbin We don't need these commands in both locations, /sbin will be sufficient. --- scripts/upd-instroot | 6 ------ 1 file changed, 6 deletions(-) diff --git a/scripts/upd-instroot b/scripts/upd-instroot index 8e86eae7f..5ac9aa27a 100755 --- a/scripts/upd-instroot +++ b/scripts/upd-instroot @@ -346,7 +346,6 @@ etc/yum/pluginconf.d/fedorakmod.conf etc/yum/pluginconf.d/whiteout.conf lib/terminfo lib/udev -sbin/*gfs* sbin/arping sbin/badblocks sbin/btrfsctl @@ -396,8 +395,6 @@ sbin/mkfs.xfs sbin/mkraid sbin/mkreiserfs sbin/mkswap -sbin/mount.nfs* -sbin/mount.ntfs* sbin/parted sbin/pcmcia-socket-startup sbin/pdisk @@ -410,7 +407,6 @@ sbin/sfdisk sbin/silo sbin/tune2fs sbin/udev* -sbin/umount.nfs* sbin/xfs_repair sbin/xfsdump sbin/xfsrestore @@ -738,8 +734,6 @@ sbin/restore sbin/rrestore sbin/rmmod sbin/route -sbin/mount.cifs -sbin/umount.cifs usr/bin/bunzip2 usr/bin/bzcat usr/bin/bzip2 -- cgit From 9255f1e75e41b60df086bd37c82e1aa77d4d42f2 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 19 Mar 2009 18:00:47 -1000 Subject: Move OUTPUT_TERMINAL definition to isys.h --- isys/iface.c | 1 + isys/iface.h | 1 - isys/isys.h | 2 ++ 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/isys/iface.c b/isys/iface.c index fd9459dc7..1df5fa0cd 100644 --- a/isys/iface.c +++ b/isys/iface.c @@ -50,6 +50,7 @@ #include #include +#include "isys.h" #include "iface.h" #include "str.h" diff --git a/isys/iface.h b/isys/iface.h index 59e384914..c95c39bd6 100644 --- a/isys/iface.h +++ b/isys/iface.h @@ -46,7 +46,6 @@ enum { IPV6_UNUSED_METHOD, IPV6_AUTO_METHOD, IPV6_DHCP_METHOD, /* Macros for starting NetworkManager */ #define NETWORKMANAGER "/usr/sbin/NetworkManager" -#define OUTPUT_TERMINAL "/dev/tty5" /* Per-interface configuration information */ typedef struct _iface_t { diff --git a/isys/isys.h b/isys/isys.h index 6b0695131..43355ca38 100644 --- a/isys/isys.h +++ b/isys/isys.h @@ -28,6 +28,8 @@ #define EARLY_SWAP_RAM 270000 #endif +#define OUTPUT_TERMINAL "/dev/tty5" + int insmod(char * modName, char * path, char ** args); int rmmod(char * modName); -- cgit From 257f4aeb8c0e2f5c9d3b4595eeb486f093a2219e Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 19 Mar 2009 18:03:49 -1000 Subject: If we have no error string, place None in the tuple. Be more defensive about what we get back from the _isys module. Do not assume we get an error string. If it's empty, place None in the tuple. --- isys/isys.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/isys/isys.c b/isys/isys.c index 0813e5054..65ee688b3 100644 --- a/isys/isys.c +++ b/isys/isys.c @@ -291,7 +291,14 @@ static PyObject * doMount(PyObject * s, PyObject * args) { PyObject *tuple = PyTuple_New(2); PyTuple_SetItem(tuple, 0, PyInt_FromLong(rc)); - PyTuple_SetItem(tuple, 1, PyString_FromString(err)); + + if (err == NULL) { + Py_INCREF(Py_None); + PyTuple_SetItem(tuple, 1, Py_None); + } else { + PyTuple_SetItem(tuple, 1, PyString_FromString(err)); + } + PyErr_SetObject(PyExc_SystemError, tuple); } -- cgit From e85bafd5f6a56a9d240cb06c11df484f5e1c4378 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 19 Mar 2009 18:13:21 -1000 Subject: Catch FSError when detecting storage, prevent user from continuing. This could use expansion in the future, perhaps removing the disk that caused the problem as a Skip option, or something like that. For now, if we can't read your disks, stop. --- storage/__init__.py | 40 ++++++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 16 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index c5d3e4718..4e8fc4bd3 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -222,22 +222,30 @@ class Storage(object): if device.format.type == "luks" and device.format.exists: self.__luksDevs[device.format.uuid] = device.format._LUKS__passphrase - w = self.anaconda.intf.waitWindow(_("Finding Devices"), - _("Finding storage devices...")) - self.iscsi.startup(self.anaconda.intf) - self.zfcp.startup() - self.devicetree = DeviceTree(intf=self.anaconda.intf, - ignored=self.ignoredDisks, - exclusive=self.exclusiveDisks, - clear=self.clearPartDisks, - reinitializeDisks=self.reinitializeDisks, - protected=self.protectedPartitions, - zeroMbr=self.zeroMbr, - passphrase=self.encryptionPassphrase, - luksDict=self.__luksDevs) - self.devicetree.populate() - self.fsset = FSSet(self.devicetree) - w.pop() + try: + w = self.anaconda.intf.waitWindow(_("Finding Devices"), + _("Finding storage devices...")) + self.iscsi.startup(self.anaconda.intf) + self.zfcp.startup() + self.devicetree = DeviceTree(intf=self.anaconda.intf, + ignored=self.ignoredDisks, + exclusive=self.exclusiveDisks, + clear=self.clearPartDisks, + reinitializeDisks=self.reinitializeDisks, + protected=self.protectedPartitions, + zeroMbr=self.zeroMbr, + passphrase=self.encryptionPassphrase, + luksDict=self.__luksDevs) + self.devicetree.populate() + self.fsset = FSSet(self.devicetree) + w.pop() + except FSError as e: + self.anaconda.intf.messageWindow(_("Error"), + _("Filesystem error detected, cannot continue."), + custom_icon="error") + sys.exit(0) + finally: + w.pop() @property def devices(self): -- cgit From 993e2ef14b87a58108bcb476fbee3cd3095fb4fe Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Thu, 19 Mar 2009 18:59:07 -1000 Subject: New version. --- anaconda.spec | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index 324c5d4bb..73820dffb 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.33 +Version: 11.5.0.34 Release: 1 License: GPLv2+ Group: Applications/System @@ -210,6 +210,34 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Thu Mar 19 2009 David Cantrell - 11.5.0.34-1 +- Catch FSError when detecting storage, prevent user from continuing. + (dcantrell) +- If we have no error string, place None in the tuple. (dcantrell) +- Move OUTPUT_TERMINAL definition to isys.h (dcantrell) +- mount and umount commands are in /sbin now, remove from /usr/sbin + (dcantrell) +- Avoid SIGSEGV in doPwMount() when NULL is last parameter (#491192) + (dcantrell) +- Attempt disk commits 5 times before raising an exception. (dcantrell) +- Add boot partition size limit properties and size validation method. + (dlehman) +- Make sure boot flag gets set. (#491170) (dlehman) +- Make bootable a property of PartitionDevice. (dlehman) +- After setting up our random UUID, inform the storage layer (katzj) +- Handle system crappyness. (jgranado) +- Fix up checking for live image backing (katzj) +- Let's not remove our mountpoints (katzj) +- Fix writing the default= line in grub.conf (#490756). (clumens) +- Revert "Fix pruning of destroy actions for preexisting devices." (dlehman) +- Add more blacklisting (katzj) +- Blacklist the live image backing device (katzj) +- Move blockdev blacklisting to be a function (katzj) +- Inhibit devkit-disks during a live install (katzj) +- try to unmount everything from /media on live installs (katzj) +- Fix live installs to not traceback (katzj) +- Fix New partition in UI (rvykydal) + * Thu Mar 19 2009 David Lehman - 11.5.0.33-1 - Rework the lvm dialog. (#490301,#490966,#490681,#489870) (dlehman) - Improve chances of uniqueness from Storage.createSuggestedLVName. (dlehman) -- cgit From e2c65911008d66a5e41f86315ef18dfa7acf53af Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Thu, 19 Mar 2009 19:05:01 +0100 Subject: Fix traceback when editing encrypted mdraid device in UI. Also correct some references to self.origrequest. --- iw/raid_dialog_gui.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index 578e3e7b3..c8062047f 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -149,6 +149,7 @@ class RaidEditor: migrate = None model = self.raidlist.get_model() iter = model.get_iter_first() + format = None while iter: val = model.get_value(iter, 0) part = model.get_value(iter, 1) @@ -254,13 +255,13 @@ class RaidEditor: if self.fsoptionsDict.has_key("migratecb") and \ self.fsoptionsDict["migratecb"].get_active(): - if origrequest.format.type == "luks": + if self.origrequest.format.type == "luks": try: - usedev = self.storage.devicetree.getChildren(origrequest)[0] + usedev = self.storage.devicetree.getChildren(self.origrequest)[0] except IndexError: - usedev = origrequest + usedev = self.origrequest else: - usedev = origrequest + usedev = self.origrequest migrate = True if self.origrequest.format.exists and \ -- cgit From 0d58e68ea03011ec3ef0987ff5cc246ed70f2c11 Mon Sep 17 00:00:00 2001 From: Martin Sivak Date: Thu, 19 Mar 2009 15:41:03 +0100 Subject: Do not traceback at the very beginning of rescue mode --- anaconda | 9 +++++++++ rescue.py | 6 +++--- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/anaconda b/anaconda index a4ed591e0..3bbf0fec6 100755 --- a/anaconda +++ b/anaconda @@ -778,8 +778,17 @@ if __name__ == "__main__": if opts.ksfile: anaconda.isKickstart = True instClass.setInstallData(anaconda) + + #we need waitWindow valid in processKickstartFile. because storage uses it + from snack import * + screen = SnackScreen() + anaconda.intf = rescue.RescueInterface(screen) + kickstart.processKickstartFile(anaconda, opts.ksfile) + anaconda.intf = None + screen.finish() + # command line 'nomount' overrides kickstart /same for vnc/ anaconda.rescue_mount = not (opts.rescue_nomount or anaconda.id.ksdata.rescue.nomount) diff --git a/rescue.py b/rescue.py index aeceb9803..688266f41 100644 --- a/rescue.py +++ b/rescue.py @@ -223,15 +223,15 @@ def runRescue(anaconda, instClass): sys.exit(0) + screen = SnackScreen() + anaconda.intf = RescueInterface(screen) + if anaconda.isKickstart: if anaconda.id.ksdata.rescue and anaconda.id.ksdata.rescue.romount: readOnly = 1 else: readOnly = 0 else: - screen = SnackScreen() - anaconda.intf = RescueInterface(screen) - # prompt to see if we should try and find root filesystem and mount # everything in /etc/fstab on that root rc = ButtonChoiceWindow(screen, _("Rescue"), -- cgit From 4f42f8ea3a4431fc66f39d6410f1b8a4186b700f Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 20 Mar 2009 11:32:32 -0400 Subject: Fix a reference to the partitions list (#491335). --- platform.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform.py b/platform.py index 330601adb..f1a20a471 100644 --- a/platform.py +++ b/platform.py @@ -235,7 +235,7 @@ class IPSeriesPPC(PPC): bootDev = None # We want the first PReP partition. - for device in storage.partitions: + for device in self.anaconda.id.storage.partitions: if device.partedPartition.getFlag(parted.PARTITION_PREP): bootDev = device break -- cgit From e4c81910ee1c7223c8c8697400d0e628f095500f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 20 Mar 2009 10:47:48 +0100 Subject: Make root= line in grub.conf and path spec in fstab consistent booty was using its own code to find out wether to pass in UUID=.... or /dev/.... as root= option, leading to inconsistencies between fstab (/dev/mapper/Volgroup-lv_root) and grub.conf (UUID=...) Harmless, but confusing. This patch fixes this by removing the no longer needed getRootDevName method from booty and instead using device.fstabSpec --- booty/alpha.py | 2 +- booty/bootloaderInfo.py | 16 +--------------- booty/ppc.py | 2 +- booty/s390.py | 2 +- booty/sparc.py | 2 +- booty/x86.py | 3 +-- 6 files changed, 6 insertions(+), 21 deletions(-) diff --git a/booty/alpha.py b/booty/alpha.py index ca715560d..461086c1e 100644 --- a/booty/alpha.py +++ b/booty/alpha.py @@ -86,7 +86,7 @@ class alphaBootloaderInfo(bootloaderInfo): if os.path.isfile(instRoot + initrd): f.write(" initrd=%sinitrd%s.img" %(kernelPath, kernelTag)) - realroot = getRootDevName(instRoot+initrd, rootDevice) + realroot = rootDevice.fstabSpec f.write(" root=%s" %(realroot,)) args = self.args.get() diff --git a/booty/bootloaderInfo.py b/booty/bootloaderInfo.py index 8419645fa..ff9592ecb 100644 --- a/booty/bootloaderInfo.py +++ b/booty/bootloaderInfo.py @@ -82,20 +82,6 @@ def rootIsDevice(dev): return False return True -# hackery to determine if we should do root=LABEL=/ or whatnot -# as usual, knows too much about anaconda -def getRootDevName(initrd, rootDevice): - if not os.access(initrd, os.R_OK): - return rootDevice.path - - try: - if rootDevice.format.uuid: - return "UUID=%s" % rootDevice.format.uuid - except: - pass - - return rootDevice.path - class KernelArguments: def get(self): @@ -359,7 +345,7 @@ class bootloaderInfo: sl.addEntry("read-only") append = "%s" %(self.args.get(),) - realroot = getRootDevName(instRoot+initrd, rootDev) + realroot = rootDev.fstabSpec if rootIsDevice(realroot): sl.addEntry("root", rootDev.path) else: diff --git a/booty/ppc.py b/booty/ppc.py index 7ac9299a3..706f1989b 100644 --- a/booty/ppc.py +++ b/booty/ppc.py @@ -121,7 +121,7 @@ class ppcBootloaderInfo(bootloaderInfo): append = "%s" %(self.args.get(),) - realroot = getRootDevName(instRoot+initrd, rootDev) + realroot = rootDev.fstabSpec if rootIsDevice(realroot): f.write("\troot=%s\n" %(realroot,)) else: diff --git a/booty/s390.py b/booty/s390.py index 8cddb053d..575ab2813 100644 --- a/booty/s390.py +++ b/booty/s390.py @@ -144,7 +144,7 @@ class s390BootloaderInfo(bootloaderInfo): if os.access (instRoot + initrd, os.R_OK): f.write('\tramdisk=%sinitrd%s.img\n' %(self.kernelLocation, kernelTag)) - realroot = getRootDevName(instRoot+initrd, rootDev) + realroot = rootDev.fstabSpec f.write('\tparameters="root=%s' %(realroot,)) if bl.args.get(): f.write(' %s' % (bl.args.get())) diff --git a/booty/sparc.py b/booty/sparc.py index 4ea2ceaf5..b094a4512 100644 --- a/booty/sparc.py +++ b/booty/sparc.py @@ -62,7 +62,7 @@ class sparcBootloaderInfo(bootloaderInfo): append = "%s" % (self.args.get(),) - realroot = getRootDevName(instRoot+initrd, rootDev) + realroot = rootDev.fstabSpec if rootIsDevice(realroot): f.write("\troot=%s\n" % (realroot,)) else: diff --git a/booty/x86.py b/booty/x86.py index 221186a41..d710fbb8e 100644 --- a/booty/x86.py +++ b/booty/x86.py @@ -237,8 +237,7 @@ class x86BootloaderInfo(efiBootloaderInfo): f.write('title %s (%s)\n' % (longlabel, version)) f.write('\troot %s\n' % self.grubbyPartitionName(bootDevs[0])) - realroot = getRootDevName(instRoot+initrd, rootDev) - realroot = " root=%s" %(realroot,) + realroot = " root=%s" % rootDev.fstabSpec if version.endswith("xen0") or (version.endswith("xen") and not os.path.exists("/proc/xen")): # hypervisor case -- cgit From 1edec1afdf9e98b912048f35c8da4970166e7c7f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 20 Mar 2009 10:51:33 +0100 Subject: Do not use _rnetdev as fstab option for network based / In RHEL-5 we use _rnetdev for network based /, and various pieces of the RHEL-5 initscripts key of on this to do special things. So I thought it would be good to do the same in Fedora. Testing has revealed that Fedora's mount command cannot handle this options on things go boom, so use plain _netdev for / just like we do with other network based fs, and as we've been doing in previous Fedora releases. --- storage/__init__.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 4e8fc4bd3..fdb8c9197 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1658,10 +1658,7 @@ class FSSet(object): options = options or "defaults" for netdev in netdevs: if device.dependsOn(netdev): - if mountpoint == "/": - options = options + ",_rnetdev" - else: - options = options + ",_netdev" + options = options + ",_netdev" break devspec = device.fstabSpec dump = device.format.dump -- cgit From ff59b716da6b3760e5b8a943d911b4b56a10350b Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 20 Mar 2009 15:27:18 -0400 Subject: Fix text mode autopartitioning (#491282). (1) We need to use the device's name instead of its path for clearPartDisks. (2) We need to set doAutoPart since text mode is autopart only. --- textw/partition_text.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/textw/partition_text.py b/textw/partition_text.py index d74c7d491..472a5cea3 100644 --- a/textw/partition_text.py +++ b/textw/partition_text.py @@ -106,7 +106,7 @@ class PartitionTypeWindow: selected = 0 sizestr = "%8.0f MB" % (disk.size,) - diskdesc = "%6s %s (%s)" % (disk.path, sizestr, model[:24],) + diskdesc = "%6s %s (%s)" % (disk.name, sizestr, model[:23],) drivelist.append(diskdesc, selected = selected) @@ -139,6 +139,7 @@ class PartitionTypeWindow: continue anaconda.dispatch.skipStep("autopartitionexecute", skip = 0) + anaconda.id.storage.doAutoPart = True anaconda.id.storage.clearPartType = partmethod_ans anaconda.id.storage.clearPartDisks = sel break -- cgit From fcdfd3a0ab3d02c5a9bdafb49acc035844421a55 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Fri, 20 Mar 2009 15:55:19 -0400 Subject: Make some fixes to the rescue mode system selection UI (#489973, #489977). (1) When there's more than one system to choose from, display the name of the device, its release string, and the label if it exists. (2) Return the right type when the user makes a selection from the combo box. --- rescue.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/rescue.py b/rescue.py index 688266f41..fdd78475f 100644 --- a/rescue.py +++ b/rescue.py @@ -273,7 +273,10 @@ def runRescue(anaconda, instClass): devList = [] for (device, relstr) in disks: - devList.append(device.path) + if getattr(device.format, "label", None): + devList.append("%s (%s) - %s" % (device.name, device.format.label, relstr)) + else: + devList.append("%s - %s" % (device.name, relstr)) (button, choice) = \ ListboxChoiceWindow(screen, _("System to Rescue"), @@ -286,7 +289,7 @@ def runRescue(anaconda, instClass): if button == string.lower (_("Exit")): root = None else: - root = disks[choice][0] + root = disks[choice] rootmounted = 0 -- cgit From 7198caf60aa5bba5172db688ac921a1a42591b0e Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Fri, 20 Mar 2009 10:16:03 -1000 Subject: Revert "mount and umount commands are in /sbin now, remove from /usr/sbin" This reverts commit 8683176aef29562f6e7505a7727b79f94e1c8a8c. --- scripts/upd-instroot | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/scripts/upd-instroot b/scripts/upd-instroot index 5ac9aa27a..8e86eae7f 100755 --- a/scripts/upd-instroot +++ b/scripts/upd-instroot @@ -346,6 +346,7 @@ etc/yum/pluginconf.d/fedorakmod.conf etc/yum/pluginconf.d/whiteout.conf lib/terminfo lib/udev +sbin/*gfs* sbin/arping sbin/badblocks sbin/btrfsctl @@ -395,6 +396,8 @@ sbin/mkfs.xfs sbin/mkraid sbin/mkreiserfs sbin/mkswap +sbin/mount.nfs* +sbin/mount.ntfs* sbin/parted sbin/pcmcia-socket-startup sbin/pdisk @@ -407,6 +410,7 @@ sbin/sfdisk sbin/silo sbin/tune2fs sbin/udev* +sbin/umount.nfs* sbin/xfs_repair sbin/xfsdump sbin/xfsrestore @@ -734,6 +738,8 @@ sbin/restore sbin/rrestore sbin/rmmod sbin/route +sbin/mount.cifs +sbin/umount.cifs usr/bin/bunzip2 usr/bin/bzcat usr/bin/bzip2 -- cgit From b49ed352f17102f5d5a1c411fe1f380314c93940 Mon Sep 17 00:00:00 2001 From: Jeremy Katz Date: Fri, 20 Mar 2009 15:37:12 -0400 Subject: Need to notify the kernel of changes before udev settle Not sure how this worked yesterday, but order is important --- livecd.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/livecd.py b/livecd.py index 96610cd1b..c6ed037cb 100644 --- a/livecd.py +++ b/livecd.py @@ -240,9 +240,10 @@ class LiveCDCopyBackend(backend.AnacondaBackend): stderr="/dev/tty5", searchPath = 1) # and now set the uuid in the storage layer - storage.udev.udev_settle() rootDevice.updateSysfsPath() iutil.notify_kernel("/sys%s" %rootDevice.sysfsPath) + storage.udev.udev_settle() + rootDevice.updateSysfsPath() info = storage.udev.udev_get_block_device("/sys%s" % rootDevice.sysfsPath) rootDevice.format.uuid = storage.udev.udev_device_get_uuid(info) log.info("reset the rootdev (%s) to have a uuid of %s" %(rootDevice.sysfsPath, rootDevice.format.uuid)) -- cgit From e40e27ac30d8a433d1a309cdc7ce8c0cc959d6be Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 20 Mar 2009 13:22:34 -0500 Subject: Use label attr instead of non-existent fslabel attr. (#491120) --- storage/formats/fs.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index c624c592d..193943413 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -539,7 +539,7 @@ class FS(DeviceFormat): if rc: raise FSError("label failed") - self.fslabel = label + self.label = label self.notifyKernel() @property @@ -828,8 +828,8 @@ class BTRFS(FS): if options and isinstance(options, list): argv.extend(options) argv.extend(self.defaultFormatOptions) - if self.fslabel: - argv.extend(["-L", self.fslabel]) + if self.label: + argv.extend(["-L", self.label]) argv.append(self.device) return argv -- cgit From 578eff6629b00d3d089a4c6f24f7a7c8cecdcdb1 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Fri, 20 Mar 2009 10:34:20 -1000 Subject: Reset mouse pointer if we find an unreadable disk. Without the call to busyCursorPop(), we get the watch mouse pointer for the rest of the install. waitWindow sets it up, which is called when 'Finding storage devices...' is displayed. --- storage/devicetree.py | 1 + 1 file changed, 1 insertion(+) diff --git a/storage/devicetree.py b/storage/devicetree.py index ae6bed9b4..f8b107d6e 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -122,6 +122,7 @@ def questionInitializeDisk(intf=None, name=None): if not intf or not name: pass else: + intf.icw.busyCursorPop() rc = intf.messageWindow(_("Warning"), _("Error processing drive %s.\n" "Maybe it needs to be reinitialized." -- cgit From 49bc0ee23e0e827b79f141575fe1bddc1a50926f Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Wed, 18 Mar 2009 11:57:03 -1000 Subject: Make PartitionDevice resize work. Resizing existing filesystems on PartitionDevice objects now works properly. I have resized ext3 and ntfs on existing partitions and autopart takes over after that. LVM resizing is not done yet, but this patch takes care of the major case: resizing existing Windows installations. --- storage/devices.py | 61 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index e1f08d378..ca3246296 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -939,28 +939,17 @@ class PartitionDevice(StorageDevice): return s def _setTargetSize(self, newsize): - # a change in the target size means we need to resize the disk - # if targetSize is 0, it means we are initializing, so don't jump - # the gun and assume we're resizing - if newsize != self.targetSize and self.targetSize != 0: - self._resize = True - - self._targetSize = newsize - - if self._resize: - currentGeom = self.partedPartition.geometry - currentDev = currentGeom.device - newLen = long(self.targetSize * 1024 * 1024) / currentDev.sectorSize - geom = parted.Geometry(device=currentDev, - start=currentGeom.start, - length=newLen) - constraint = parted.Constraint(exactGeom=geom) - - partedDisk = self.disk.partedDisk - partedDisk.setPartitionGeometry(partition=self.partedPartition, - constraint=constraint, - start=geom.start, end=geom.end) - self.partedPartition = None + if newsize != self.currentSize: + # change this partition's geometry in-memory so that other + # partitioning operations can complete (e.g., autopart) + self._targetSize = newsize + disk = self.disk.partedDisk + + # resize the partition's geometry in memory + (constraint, geometry) = self._computeResize(self.partedPartition) + disk.setPartitionGeometry(partition=self.partedPartition, + constraint=constraint, + start=geometry.start, end=geometry.end) @property def path(self): @@ -1146,6 +1135,20 @@ class PartitionDevice(StorageDevice): self.exists = True self.setup() + def _computeResize(self, partition): + log_method_call(self, self.name, status=self.status) + + # compute new size for partition + currentGeom = partition.geometry + currentDev = currentGeom.device + newLen = long(self.targetSize * 1024 * 1024) / currentDev.sectorSize + newGeometry = parted.Geometry(device=currentDev, + start=currentGeom.start, + length=newLen) + constraint = parted.Constraint(exactGeom=newGeometry) + + return (constraint, newGeometry) + def resize(self, intf=None): """ Resize the device. @@ -1153,8 +1156,20 @@ class PartitionDevice(StorageDevice): """ log_method_call(self, self.name, status=self.status) - if self._resize: + if self.targetSize != self.currentSize: + # partedDisk has been restored to _origPartedDisk, so + # recalculate resize geometry because we may have new + # partitions on the disk, which could change constraints + partition = self.disk.partedDisk.getPartitionByPath(self.path) + (constraint, geometry) = self._computeResize(partition) + + self.disk.partedDisk.setPartitionGeometry(partition=partition, + constraint=constraint, + start=geometry.start, + end=geometry.end) + self.disk.commit() + self.notifyKernel() def destroy(self): """ Destroy the device. """ -- cgit From 29701147ff691c168dbef8682ef3ed6a811819ad Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Fri, 20 Mar 2009 10:19:43 -1000 Subject: Do not include .h and .sh files in updates.img --- Makefile | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile index 2555181f6..d5fa03241 100644 --- a/Makefile +++ b/Makefile @@ -185,6 +185,7 @@ updates: fi ; \ git diff --stat $(ARCHIVE_TAG) | grep " | " | \ grep -v "\.spec" | grep -v "Makefile" | grep -v "\.c\ " | \ + grep -v "\.h" | grep -v "\.sh" | \ while read sourcefile stuff ; do \ dn="$$(echo $$sourcefile | cut -d '/' -f 1)" ; \ case $$dn in \ -- cgit From 098449b0c7b40f425cb232abaf5bd94bf873e8ed Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 20 Mar 2009 22:46:00 -0500 Subject: Fix traceback on upgrade. (#491446) --- packages.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages.py b/packages.py index 26eb7dfa0..03e21b324 100644 --- a/packages.py +++ b/packages.py @@ -108,7 +108,7 @@ def turnOnFilesystems(anaconda): upgrade_migrate = False if anaconda.id.upgrade: for d in anaconda.id.storage.fsset.migratableDevices: - if d.migrate: + if d.format.migrate: upgrade_migrate = True try: -- cgit From 79eb49ef19fdd8a827c7006eb2666655539dc09b Mon Sep 17 00:00:00 2001 From: David Lehman Date: Fri, 20 Mar 2009 22:58:39 -0500 Subject: Fix traceback in FSSet.crypttab. (#491160) --- storage/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/__init__.py b/storage/__init__.py index fdb8c9197..0600268d1 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1591,7 +1591,7 @@ class FSSet(object): devices = self.mountpoints.values() + self.swapDevices # prune crypttab -- only mappings required by one or more entries - for name in self.cryptTab.mappings: + for name in self.cryptTab.mappings.keys(): keep = False mapInfo = self.cryptTab[name] cryptoDev = mapInfo['device'] -- cgit From 04f379766917918f30a0d2386f5276a1b5ae15ed Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Fri, 20 Mar 2009 18:02:21 -1000 Subject: New version. --- anaconda.spec | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index 73820dffb..5aa18f09b 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.34 +Version: 11.5.0.35 Release: 1 License: GPLv2+ Group: Applications/System @@ -210,6 +210,25 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Fri Mar 20 2009 David Cantrell - 11.5.0.35-1 +- Fix traceback in FSSet.crypttab. (#491160) (dlehman) +- Fix traceback on upgrade. (#491446) (dlehman) +- Do not include .h and .sh files in updates.img (dcantrell) +- Make PartitionDevice resize work. (dcantrell) +- Reset mouse pointer if we find an unreadable disk. (dcantrell) +- Use label attr instead of non-existent fslabel attr. (#491120) (dlehman) +- Need to notify the kernel of changes before udev settle (katzj) +- Revert "mount and umount commands are in /sbin now, remove from /usr/sbin" + (dcantrell) +- Make some fixes to the rescue mode system selection UI (#489973, #489977). + (clumens) +- Fix text mode autopartitioning (#491282). (clumens) +- Do not use _rnetdev as fstab option for network based / (hdegoede) +- Make root= line in grub.conf and path spec in fstab consistent (hdegoede) +- Fix a reference to the partitions list (#491335). (clumens) +- Do not traceback at the very beginning of rescue mode (msivak) +- Fix traceback when editing encrypted mdraid device in UI. (rvykydal) + * Thu Mar 19 2009 David Cantrell - 11.5.0.34-1 - Catch FSError when detecting storage, prevent user from continuing. (dcantrell) -- cgit From 978533e996dabdab2e07b07a980e3decb29312b3 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 23 Mar 2009 10:00:50 -0400 Subject: Add a missing import (#491605). --- kickstart.py | 1 + 1 file changed, 1 insertion(+) diff --git a/kickstart.py b/kickstart.py index dfabd79b9..802cb4350 100644 --- a/kickstart.py +++ b/kickstart.py @@ -18,6 +18,7 @@ # along with this program. If not, see . # +from storage.deviceaction import * from storage.devices import LUKSDevice from storage.devicelibs.lvm import getPossiblePhysicalExtents from storage.formats import getFormat -- cgit From 589ac766cc2b41860608773db41de61a658a6b32 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 23 Mar 2009 10:03:58 -0400 Subject: Fix the import of checkbootloader (#491574). --- bootloader.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/bootloader.py b/bootloader.py index 08e874ce3..b9d995e4d 100644 --- a/bootloader.py +++ b/bootloader.py @@ -36,7 +36,7 @@ import logging log = logging.getLogger("anaconda") import booty -from booty import bootloaderInfo +from booty import bootloaderInfo, checkbootloader def bootloaderSetupChoices(anaconda): if anaconda.dir == DISPATCH_BACK: @@ -120,7 +120,6 @@ def writeBootloader(anaconda): # now make the upgrade stuff work for kickstart too. ick. if anaconda.isKickstart and anaconda.id.bootloader.doUpgradeOnly: - import checkbootloader (bootType, theDev) = checkbootloader.getBootloaderTypeAndBoot(anaconda.rootPath, storage=anaconda.id.storage) anaconda.id.bootloader.doUpgradeonly = 1 -- cgit From e08ace84f10b9eb9acb6d71326359ce42ff69477 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 23 Mar 2009 10:28:03 -0400 Subject: If there was an exception leading to the urlgrabber error, log it. --- yuminstall.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/yuminstall.py b/yuminstall.py index 19f31be4c..89f36686e 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -694,7 +694,10 @@ class AnacondaYum(YumSorter): self.currentMedia = None def urlgrabberFailureCB (self, obj, *args, **kwargs): - log.warning("Try %s/%s for %s failed" % (obj.tries, obj.retry, obj.url)) + if hasattr(obj, "exception"): + log.warning("Try %s/%s for %s failed: %s" % (obj.tries, obj.retry, obj.url, obj.exception)) + else: + log.warning("Try %s/%s for %s failed" % (obj.tries, obj.retry, obj.url)) if obj.tries == obj.retry: return -- cgit From 47dcbbcaaa1f5e026de61df887143fa119ed5da2 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Mar 2009 17:01:12 -0400 Subject: Add a fake device for bind mounting /dev. --- storage/__init__.py | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 0600268d1..526500ae8 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1120,6 +1120,7 @@ class FSSet(object): self.cryptTab = None self.blkidTab = None self.active = False + self._dev = None self._devpts = None self._sysfs = None self._proc = None @@ -1133,6 +1134,17 @@ class FSSet(object): mountpoint="/sys")) return self._sysfs + @property + def dev(self): + if not self._dev: + self._dev = DirectoryDevice("/dev", format=getFormat("bind", + device="/dev", + mountpoint="/dev", + exists=True), + exists=True) + + return self._dev + @property def devpts(self): if not self._devpts: @@ -1397,8 +1409,9 @@ class FSSet(object): skipRoot=False): intf = anaconda.intf devices = self.mountpoints.values() + self.swapDevices - devices.extend([self.devshm, self.devpts, self.sysfs, self.proc]) + devices.extend([self.dev, self.devshm, self.devpts, self.sysfs, self.proc]) devices.sort(key=lambda d: getattr(d.format, "mountpoint", None)) + for device in devices: if not device.format.mountable or not device.format.mountpoint: continue @@ -1486,12 +1499,8 @@ class FSSet(object): self.active = True def umountFilesystems(self, instPath, ignoreErrors=True, swapoff=True): - # XXX if we tracked the /dev bind mount this wouln't be necessary - if os.path.ismount("%s/dev" % instPath): - isys.umount("%s/dev" % instPath, removeDir=False) - devices = self.mountpoints.values() + self.swapDevices - devices.extend([self.devshm, self.devpts, self.sysfs, self.proc]) + devices.extend([self.dev, self.devshm, self.devpts, self.sysfs, self.proc]) devices.sort(key=lambda d: getattr(d.format, "mountpoint", None)) devices.reverse() for device in devices: -- cgit From ad910a97a40661cf721f87d0d57334fddc34f2dd Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Mar 2009 17:02:08 -0400 Subject: Fix a typo calling the superclass's constructor. --- storage/devices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devices.py b/storage/devices.py index ca3246296..c74e895c9 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -2517,7 +2517,7 @@ class FileDevice(StorageDevice): parents -- a list of required devices (Device instances) exists -- indicates whether this is an existing device """ - StorageDevice.__init__(self, name, format=format, size=size, + StorageDevice.__init__(self, path, format=format, size=size, exists=exists, parents=parents) def probe(self): -- cgit From 06efd5e8c9506c7a14abc6fcc912bae342ec1527 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Mar 2009 17:02:44 -0400 Subject: If a filesystem is already mounted, don't raise an error. This seems pretty harmless to me, and raising an error causes problems when dealing with bind mounts. --- storage/formats/fs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 193943413..3756f48b3 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -453,7 +453,7 @@ class FS(DeviceFormat): raise FSError("no mountpoint given") if self.status: - raise FSError("filesystem is already mounted") + return if not isinstance(self, NoDevFS) and not os.path.exists(self.device): raise FSError("device %s does not exist" % self.device) -- cgit From cbc95651c4e7f23ea4c41738803a8dba115dfd72 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Mar 2009 17:03:28 -0400 Subject: Bind mount formats are mountable. --- storage/formats/fs.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 3756f48b3..2f95fe2e5 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -1051,6 +1051,10 @@ register_device_format(TmpFS) class BindFS(FS): _type = "bind" + @property + def mountable(self): + return True + register_device_format(BindFS) -- cgit From 88b6db9c9d83c215af86e0b05bd4d41e49addb71 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Mar 2009 17:04:02 -0400 Subject: Not all FileDevices have parents, so don't assume. --- storage/devices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devices.py b/storage/devices.py index c74e895c9..35e9be62a 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -2534,7 +2534,7 @@ class FileDevice(StorageDevice): root = "" try: status = self.parents[0].format.status - except AttributeError: + except (AttributeError, IndexError): status = False if status: -- cgit From 407e5f91c28bea04d2a4aa70f6a15e63a4d7f094 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 18 Mar 2009 17:04:16 -0400 Subject: Let mountFilesystems handling bind mounting /dev (#490772). --- livecd.py | 6 ++---- rescue.py | 6 ------ upgrade.py | 2 -- yuminstall.py | 8 -------- 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/livecd.py b/livecd.py index c6ed037cb..391b1ce89 100644 --- a/livecd.py +++ b/livecd.py @@ -136,7 +136,7 @@ class LiveCDCopyBackend(backend.AnacondaBackend): def _unmountNonFstabDirs(self, anaconda): # unmount things that aren't listed in /etc/fstab. *sigh* - dirs = ["/dev"] + dirs = [] if flags.selinux: dirs.append("/selinux") for dir in dirs: @@ -152,13 +152,12 @@ class LiveCDCopyBackend(backend.AnacondaBackend): swapoff = False) os.rmdir(anaconda.rootPath) except Exception, e: - log.error("Unable to unmount filesystems.") + log.error("Unable to unmount filesystems: %s" % e) def doPreInstall(self, anaconda): if anaconda.dir == DISPATCH_BACK: self._unmountNonFstabDirs(anaconda) return - anaconda.id.storage.fsset.umountFilesystems(anaconda.rootPath, swapoff = False) @@ -312,7 +311,6 @@ class LiveCDCopyBackend(backend.AnacondaBackend): isys.mount("/selinux", anaconda.rootPath + "/selinux", "selinuxfs") except Exception, e: log.error("error mounting selinuxfs: %s" %(e,)) - isys.mount("/dev", "%s/dev" %(anaconda.rootPath,), bindMount = True) wait.pop() diff --git a/rescue.py b/rescue.py index fdd78475f..7d480e7f1 100644 --- a/rescue.py +++ b/rescue.py @@ -331,12 +331,6 @@ def runRescue(anaconda, instClass): except: log.error("Error enabling swap") - # now that dev is udev, bind mount the installer dev there - isys.mount("/dev", "%s/dev" %(anaconda.rootPath,), bindMount = True) - - # and /dev/pts - isys.mount("/dev/pts", "%s/dev/pts" %(anaconda.rootPath,), bindMount = True) - # and /selinux too if flags.selinux and os.path.isdir("%s/selinux" %(anaconda.rootPath,)): try: diff --git a/upgrade.py b/upgrade.py index 7cb2f5dab..c080c8651 100644 --- a/upgrade.py +++ b/upgrade.py @@ -277,8 +277,6 @@ def upgradeMountFilesystems(anaconda): message = message + '\t' + n + '\n' anaconda.intf.messageWindow(_("Invalid Directories"), message) sys.exit(0) - - bindMountDevDirectory(anaconda.rootPath) else: if not os.access (anaconda.rootPath + "/etc/fstab", os.R_OK): anaconda.intf.messageWindow(_("Warning"), diff --git a/yuminstall.py b/yuminstall.py index 89f36686e..963303256 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -1457,14 +1457,6 @@ reposdir=/etc/anaconda.repos.d,/tmp/updates/anaconda.repos.d,/tmp/product/anacon except Exception, e: log.error("error mounting selinuxfs: %s" %(e,)) - # we need to have a /dev during install and now that udev is - # handling /dev, it gets to be more fun. so just bind mount the - # installer /dev - log.warning("no dev package, going to bind mount /dev") - isys.mount("/dev", "%s/dev" %(anaconda.rootPath,), bindMount = True) - if not upgrade: - anaconda.id.storage.fsset.mkDevRoot(anaconda.rootPath) - # write out the fstab if not upgrade: anaconda.id.storage.fsset.write(anaconda.rootPath) -- cgit From 2e84e241d7bfea1ab22935256ca0e5a24ef7ebcb Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 23 Mar 2009 15:56:53 -0400 Subject: If the new size and old size are the same, treat as a no-op (#491496). There's no sense raising a traceback if the user just chose the same size as the filesystem already is. --- iw/partition_dialog_gui.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/iw/partition_dialog_gui.py b/iw/partition_dialog_gui.py index 45d940958..71c7333b3 100644 --- a/iw/partition_dialog_gui.py +++ b/iw/partition_dialog_gui.py @@ -238,9 +238,13 @@ class PartitionEditor: if self.fsoptionsDict.has_key("resizecb") and \ self.fsoptionsDict["resizecb"].get_active(): size = self.fsoptionsDict["resizesb"].get_value_as_int() - actions.append(ActionResizeDevice(request, size)) - if request.format.type: - actions.append(ActionResizeFormat(request, size)) + + try: + actions.append(ActionResizeDevice(request, size)) + if request.format.type: + actions.append(ActionResizeFormat(request, size)) + except ValueError: + pass if request.format.exists and \ getattr(request, "mountpoint", None) and \ -- cgit From bfa234b45d3563ea4661cfddd6b7171777c94bb0 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Mon, 23 Mar 2009 16:01:06 -0400 Subject: Pop the busy cursor when we're done with the wait window (#491736). --- storage/__init__.py | 3 +++ storage/devicetree.py | 1 - 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/storage/__init__.py b/storage/__init__.py index 526500ae8..3c1394c73 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -240,6 +240,9 @@ class Storage(object): self.fsset = FSSet(self.devicetree) w.pop() except FSError as e: + if w: + w.pop() + self.anaconda.intf.messageWindow(_("Error"), _("Filesystem error detected, cannot continue."), custom_icon="error") diff --git a/storage/devicetree.py b/storage/devicetree.py index f8b107d6e..ae6bed9b4 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -122,7 +122,6 @@ def questionInitializeDisk(intf=None, name=None): if not intf or not name: pass else: - intf.icw.busyCursorPop() rc = intf.messageWindow(_("Warning"), _("Error processing drive %s.\n" "Maybe it needs to be reinitialized." -- cgit From 3b1206629309d766fc25d529ae3145c9532c8977 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Mar 2009 22:05:23 -0500 Subject: Remove all implicit calls to self.format.destroy from Device classes. We are scheduling the actions we need. This is just noise now. --- storage/devices.py | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 35e9be62a..6494601a5 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -609,10 +609,6 @@ class StorageDevice(Device): if not self.isleaf: raise DeviceError("Cannot destroy non-leaf device") - if self.status: - # best effort - self.format.destroy() - self.exists = False # we already did this in DeviceTree._removeDevice #for parent in self.parents: @@ -805,9 +801,6 @@ class DiskDevice(StorageDevice): if not self.mediaPresent: raise DeviceError("cannot destroy disk %s which has no media" % self.name) - if self.status: - self.format.destroy() - self.partedDisk.deleteAllPartitions() # this is perhaps a separate operation (wiping the disklabel) self.partedDisk.clobber() @@ -1184,9 +1177,6 @@ class PartitionDevice(StorageDevice): raise DeviceError("Cannot destroy non-leaf device") self.setupParents() - if self.status: - self.format.destroy() - self.disk.removePartition(self) self.disk.commit() @@ -1970,9 +1960,6 @@ class LVMLogicalVolumeDevice(DMDevice): if not self.exists: raise DeviceError("device has not been created") - if self.status: - self.format.destroy() - self.teardown() lvm.lvremove(self.vg.name, self._name) self.exists = False @@ -2306,9 +2293,6 @@ class MDRaidArrayDevice(StorageDevice): if not self.exists: raise DeviceError("device has not been created") - if self.status: - self.format.destroy() - self.teardown() # The destruction of the formatting on the member devices does the @@ -2587,9 +2571,6 @@ class FileDevice(StorageDevice): if not self.exists: raise DeviceError("device has not been created") - if self.status: - self.format.destroy() - os.unlink(self.path) self.exists = False -- cgit From faee0d96c7a213acfa4edcba198ad4a398110282 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Mon, 23 Mar 2009 22:16:35 -0500 Subject: Add EFI, Apple Bootstrap, and PPC PReP Boot formats. --- iw/lvm_dialog_gui.py | 2 +- iw/partition_ui_helpers_gui.py | 2 +- platform.py | 13 ++++++----- storage/devices.py | 4 ++++ storage/devicetree.py | 12 ++++++++++ storage/formats/fs.py | 44 +++++++++++++++++++++++++---------- storage/formats/prepboot.py | 52 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 109 insertions(+), 20 deletions(-) create mode 100644 storage/formats/prepboot.py diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index 5b07b5807..5bb073213 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -417,7 +417,7 @@ class VolumeGroupEditor: newfstypeCombo = createFSTypeMenu(format, fstypechangeCB, mountCombo, - ignorefs = ["software RAID", "physical volume (LVM)", "vfat", "PPC PReP Boot", "hfs"]) + ignorefs = ["software RAID", "physical volume (LVM)", "efi", "PPC PReP Boot", "Apple Bootstrap"]) lbl.set_mnemonic_widget(newfstypeCombo) maintable.attach(newfstypeCombo, 1, 2, row, row + 1) row += 1 diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index 5cd75c20a..fb311df44 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -229,7 +229,7 @@ def createFSTypeMenu(format, fstypechangeCB, mountCombo, def mountptchangeCB(widget, fstypecombo): if iutil.isEfi() and widget.get_children()[0].get_text() == "/boot/efi": - fstypecombo.set_active_text("efi") + fstypecombo.set_active_text(getFormat("efi").name) if widget.get_children()[0].get_text() == "/boot": fstypecombo.set_active_text(get_default_filesystem_type(boot=True)) diff --git a/platform.py b/platform.py index f1a20a471..59b87273b 100644 --- a/platform.py +++ b/platform.py @@ -170,8 +170,8 @@ class EFI(Platform): if not req.format.type.startswith("ext"): raise FSError("/boot is not ext2") elif req.format.mountpoint == "/boot/efi": - if not req.format.type.endswith("fat"): - raise FSError("/boot/efi is not vfat") + if req.format.type != "efi": + raise FSError("/boot/efi is not efi") def setDefaultPartitioning(self): ret = Platform.setDefaultPartitioning(self) @@ -236,7 +236,7 @@ class IPSeriesPPC(PPC): # We want the first PReP partition. for device in self.anaconda.id.storage.partitions: - if device.partedPartition.getFlag(parted.PARTITION_PREP): + if device.format.type == "prepboot": bootDev = device break @@ -279,9 +279,10 @@ class NewWorldPPC(PPC): bootDev = None for part in self.anaconda.id.storage.partitions: - # XXX do we need to also check the size? - if part.format.type == "hfs" and self.validBootPartSize(part.size): + if part.format.type == "appleboot" and self.validBootPartSize(part.size): bootDev = part + # if we're only picking one, it might as well be the first + break return bootDev @@ -298,7 +299,7 @@ class NewWorldPPC(PPC): else: ret["boot"] = (bootDev.name, N_("Apple Bootstrap")) for (n, device) in enumerate(self.anaconda.id.storage.partitions): - if device.format.type == "hfs" and device.bootable and device.path != bootDev.path: + if device.format.type == "appleboot" and device.path != bootDev.path: ret["boot%d" % n] = (device.path, N_("Apple Bootstrap")) return ret diff --git a/storage/devices.py b/storage/devices.py index 6494601a5..064ebc14c 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -903,6 +903,10 @@ class PartitionDevice(StorageDevice): # collect information about the partition from parted self.probe() + if self.getFlag(parted.PARTITION_PREP): + # the only way to identify a PPC PReP Boot partition is to + # check the partition type/flags, so do it here. + self.format = getFormat("prepboot", device=self.path, exists=True) else: # XXX It might be worthwhile to create a shit-simple # PartitionRequest class and pass one to this constructor diff --git a/storage/devicetree.py b/storage/devicetree.py index ae6bed9b4..63339dd05 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1140,6 +1140,18 @@ class DeviceTree(object): kwargs["peStart"] = udev_device_get_pv_pe_start(info) except KeyError: log.debug("PV %s has no pe_start" % name) + elif format_type == "vfat": + # efi magic + if isinstance(device, PartitionDevice) and device.bootable: + efi = formats.getFormat("efi") + if efi.minSize <= device.size <= efi.maxSize: + args[0] = "efi" + elif format_type == "hfs": + # apple bootstrap magic + if isinstance(device, PartitionDevice) and device.bootable: + apple = formats.getFormat("appleboot") + if apple.minSize <= device.size <= apple.maxSize: + args[0] = "appleboot" format = formats.getFormat(*args, **kwargs) device.format = format diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 2f95fe2e5..d3b708278 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -783,11 +783,7 @@ register_device_format(Ext4FS) class FATFS(FS): - """ FAT filesystem. - - XXX Do we want to subclass this for EFI or twiddle bootable based - on the platform? - """ + """ FAT filesystem. """ _type = "vfat" _mkfs = "mkdosfs" _labelfs = "dosfslabel" @@ -797,15 +793,23 @@ class FATFS(FS): _packages = [ "dosfstools" ] _defaultMountOptions = ["umask=0077", "shortname=winnt"] - @property - def bootable(self): - retval = self._bootable - #if self.type in platform.bootableFSTypes: - # retval = True +register_device_format(FATFS) - return retval -register_device_format(FATFS) +class EFIFS(FATFS): + _type = "efi" + _name = "EFI System Partition" + _minSize = 50 + _maxSize = 256 + _bootable = True + + @property + def supported(self): + import platform + return (isinstance(platform.getPlatform(None), platform.EFI) + and self.utilsAvailable) + +register_device_format(EFIFS) class BTRFS(FS): @@ -928,6 +932,22 @@ class HFS(FS): register_device_format(HFS) +class AppleBootstrapFS(HFS): + _type = "appleboot" + _name = "Apple Bootstrap" + _bootable = True + _minSize = 800.00 / 1024.00 + _maxSize = 1 + + @property + def supported(self): + import platform + return (isinstance(platform.getPlatform(None), platform.NewWorldPPC) + and self.utilsAvailable) + +register_device_format(AppleBootstrapFS) + + # this doesn't need to be here class HFSPlus(FS): _type = "hfs+" diff --git a/storage/formats/prepboot.py b/storage/formats/prepboot.py new file mode 100644 index 000000000..6d2174b33 --- /dev/null +++ b/storage/formats/prepboot.py @@ -0,0 +1,52 @@ +# prepboot.py +# Format class for PPC PReP Boot. +# +# Copyright (C) 2009 Red Hat, Inc. +# +# This copyrighted material is made available to anyone wishing to use, +# modify, copy, or redistribute it subject to the terms and conditions of +# the GNU General Public License v.2, or (at your option) any later version. +# This program is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY expressed or implied, including the implied warranties of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. You should have received a copy of the +# GNU General Public License along with this program; if not, write to the +# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the +# source code or documentation are not subject to the GNU General Public +# License and may only be used or replicated with the express permission of +# Red Hat, Inc. +# +# Red Hat Author(s): Dave Lehman +# + +from ..errors import * +from . import DeviceFormat, register_device_format +from parted import PARTITION_PREP + +class PPCPRePBoot(DeviceFormat): + """ Generic device format. """ + _type = "prepboot" + _name = "PPC PReP Boot" + _udevTypes = [] + partedFlag = PARTITION_PREP + _formattable = True # can be formatted + _linuxNative = True # for clearpart + _bootable = True # can be used as boot + _maxSize = 4 # maximum size in MB + _minSize = 10 # minimum size in MB + + def __init__(self, *args, **kwargs): + """ Create a PRePBoot instance. + + Keyword Arguments: + + device -- path to the underlying device + exists -- indicates whether this is an existing format + + """ + DeviceFormat.__init__(self, *args, **kwargs) + + +register_device_format(PPCPRePBoot) + -- cgit From f92d294b45862c96e9071af557eab46750fb46c7 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Mon, 23 Mar 2009 18:28:48 -1000 Subject: New version. --- anaconda.spec | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index 5aa18f09b..75bbd2cf6 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.35 +Version: 11.5.0.36 Release: 1 License: GPLv2+ Group: Applications/System @@ -210,6 +210,25 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Mon Mar 23 2009 David Cantrell - 11.5.0.36-1 +- Add EFI, Apple Bootstrap, and PPC PReP Boot formats. (dlehman) +- Remove all implicit calls to self.format.destroy from Device classes. + (dlehman) +- Pop the busy cursor when we're done with the wait window (#491736). + (clumens) +- If the new size and old size are the same, treat as a no-op (#491496). + (clumens) +- Let mountFilesystems handling bind mounting /dev (#490772). (clumens) +- Not all FileDevices have parents, so don't assume. (clumens) +- Bind mount formats are mountable. (clumens) +- If a filesystem is already mounted, don't raise an error. (clumens) +- Fix a typo calling the superclass's constructor. (clumens) +- Add a fake device for bind mounting /dev. (clumens) +- If there was an exception leading to the urlgrabber error, log it. + (clumens) +- Fix the import of checkbootloader (#491574). (clumens) +- Add a missing import (#491605). (clumens) + * Fri Mar 20 2009 David Cantrell - 11.5.0.35-1 - Fix traceback in FSSet.crypttab. (#491160) (dlehman) - Fix traceback on upgrade. (#491446) (dlehman) -- cgit From ccfc747cd08705c0e445cb6a1df7ce1db43c53a2 Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Tue, 24 Mar 2009 10:22:08 +0100 Subject: Don't remove partitions twice. We remove the partitions at the end of clearpart function when we traverse the clearpart list and "Destroy" each partition. --- storage/partitioning.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index fdaf36021..95d6ec28d 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -318,9 +318,6 @@ def clearPartitions(storage): storage.destroyDevice(leaf) devices.remove(leaf) - # FIXME: this should be taken care of by DeviceTree.removeDevice - # or Storage.destroyDevice - part.partedPartition.disk.removePartition(part.partedPartition) log.debug("partitions: %s" % [p.getDeviceNodeName() for p in part.partedPartition.disk.partitions]) disk_name = os.path.basename(part.partedPartition.disk.device.path) if disk_name not in disks: -- cgit From 16632332b3a49994edf0016a3c5c3200d32cf6ac Mon Sep 17 00:00:00 2001 From: Martin Sivak Date: Tue, 24 Mar 2009 14:14:56 +0100 Subject: Port the dlabel feature from RHEL (#489314) --- anaconda | 4 ++ flags.py | 1 + loader/Makefile | 2 +- loader/driverdisk.c | 104 ++++++++++++++++++++++++++++++++++++++++++++++++++++ loader/driverdisk.h | 12 ++++++ loader/loader.c | 33 +++++++++++++++++ loader/loader.h | 2 + yuminstall.py | 2 +- 8 files changed, 158 insertions(+), 2 deletions(-) diff --git a/anaconda b/anaconda index 3bbf0fec6..28893e29e 100755 --- a/anaconda +++ b/anaconda @@ -251,6 +251,7 @@ def parseOptions(): op.add_option("--nomount", dest="rescue_nomount", action="store_true", default=False) op.add_option("--updates", dest="updateSrc", action="store", type="string") op.add_option("--dogtail", dest="dogtail", action="store", type="string") + op.add_option("--dlabel", action="store_true", default=False) return op.parse_args() @@ -673,6 +674,9 @@ if __name__ == "__main__": # Default is to prompt to mount the installed system. anaconda.rescue_mount = not opts.rescue_nomount + if opts.dlabel: #autodetected driverdisc in use + flags.dlabel = True + if opts.noipv4: flags.useIPv4 = False diff --git a/flags.py b/flags.py index 8dbcc9f9d..5ee0a9fe1 100644 --- a/flags.py +++ b/flags.py @@ -70,6 +70,7 @@ class Flags: self.__dict__['flags']['test'] = 0 self.__dict__['flags']['rootpath'] = 0 self.__dict__['flags']['livecdInstall'] = 0 + self.__dict__['flags']['dlabel'] = 0 self.__dict__['flags']['ibft'] = 1 self.__dict__['flags']['iscsi'] = 0 self.__dict__['flags']['serial'] = 0 diff --git a/loader/Makefile b/loader/Makefile index def2badea..cbdf03777 100644 --- a/loader/Makefile +++ b/loader/Makefile @@ -26,7 +26,7 @@ else TARGET=depend $(PROGS) endif -LIBS = -lnewt -lslang -lz -lpopt ../isys/libisys.a -lcheckisomd5 -liscsi +LIBS = -lnewt -lslang -lz -lpopt ../isys/libisys.a -lcheckisomd5 -liscsi -lblkid -luuid # devmapper LIBS += $(shell pkg-config --libs devmapper) diff --git a/loader/driverdisk.c b/loader/driverdisk.c index 9e7a30dd2..948ab9eec 100644 --- a/loader/driverdisk.c +++ b/loader/driverdisk.c @@ -48,6 +48,8 @@ #include "nfsinstall.h" #include "urlinstall.h" +#include + #include "../isys/isys.h" #include "../isys/imount.h" #include "../isys/eddsupport.h" @@ -510,6 +512,108 @@ static void loadFromLocation(struct loaderData_s * loaderData, char * dir) { busProbe(0); } +/* + * Utility functions to maintain linked-list of device names + * */ + +struct ddlist* ddlist_add(struct ddlist *list, const char* device) +{ + struct ddlist* item; + + item = (struct ddlist*)malloc(sizeof(struct ddlist)); + if(item==NULL){ + return list; + } + + item->device = strdup(device); + item->next = list; + + return item; +} + +int ddlist_free(struct ddlist *list) +{ + struct ddlist *next; + int count = 0; + + while(list!=NULL){ + next = list->next; + free(list->device); + free(list); + list = next; + count++; + } + + return count; +} + + +/* + * Look for partition with specific label (part of #316481) + */ +struct ddlist* findDriverDiskByLabel(void) +{ + char *ddLabel = "OEMDRV"; + struct ddlist *ddDevice = NULL; + blkid_cache bCache; + + int res; + blkid_dev_iterate bIter; + blkid_dev bDev; + + if(blkid_get_cache(&bCache, NULL)<0){ + logMessage(ERROR, _("Cannot initialize cache instance for blkid")); + return NULL; + } + if((res = blkid_probe_all(bCache))<0){ + logMessage(ERROR, _("Cannot probe devices in blkid: %d"), res); + return NULL; + } + + bIter = blkid_dev_iterate_begin(bCache); + blkid_dev_set_search(bIter, "LABEL", ddLabel); + while((res = blkid_dev_next(bIter, &bDev))!=0){ + bDev = blkid_verify(bCache, bDev); + if(!bDev) + continue; + logMessage(DEBUGLVL, _("Adding driver disc %s to the list of available DDs."), blkid_dev_devname(bDev)); + ddDevice = ddlist_add(ddDevice, blkid_dev_devname(bDev)); + /*blkid_free_dev(bDev); -- probably taken care of by the put cache call.. it is not exposed in the API */ + } + blkid_dev_iterate_end(bIter); + + blkid_put_cache(bCache); + + return ddDevice; +} + +int loadDriverDiskFromPartition(struct loaderData_s *loaderData, char* device) +{ + int rc; + + logMessage(INFO, "trying to mount %s", device); + if (doMultiMount(device, "/tmp/drivers", ddFsTypes, "ro", NULL)) { + logMessage(ERROR, _("Failed to mount driver disk.")); + return -1; + } + + rc = verifyDriverDisk("/tmp/drivers"); + if (rc == LOADER_BACK) { + logMessage(ERROR, _("Driver disk is invalid for this " + "release of %s."), getProductName()); + umount("/tmp/drivers"); + return -2; + } + + rc = loadDriverDisk(loaderData, "/tmp/drivers"); + umount("/tmp/drivers"); + if (rc == LOADER_BACK) { + return -3; + } + + return 0; +} + void getDDFromSource(struct loaderData_s * loaderData, char * src) { char *path = "/tmp/dd.img"; int unlinkf = 0; diff --git a/loader/driverdisk.h b/loader/driverdisk.h index e6e919dd2..0e88ca4ac 100644 --- a/loader/driverdisk.h +++ b/loader/driverdisk.h @@ -39,4 +39,16 @@ void useKickstartDD(struct loaderData_s * loaderData, int argc, void getDDFromSource(struct loaderData_s * loaderData, char * src); +int loadDriverDiskFromPartition(struct loaderData_s *loaderData, char* device); + +struct ddlist { + char* device; + struct ddlist* next; +}; + +struct ddlist* ddlist_add(struct ddlist *list, const char* device); +int ddlist_free(struct ddlist *list); + +struct ddlist* findDriverDiskByLabel(void); + #endif diff --git a/loader/loader.c b/loader/loader.c index 98ea24cba..178f9250b 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -930,6 +930,10 @@ static void parseCmdLineFlags(struct loaderData_s * loaderData, else if (!strcasecmp(argv[i], "dd") || !strcasecmp(argv[i], "driverdisk")) flags |= LOADER_FLAGS_MODDISK; + else if (!strcasecmp(argv[i], "dlabel=on")) + flags |= LOADER_FLAGS_AUTOMODDISK; + else if (!strcasecmp(argv[i], "dlabel=off")) + flags &= ~LOADER_FLAGS_AUTOMODDISK; else if (!strcasecmp(argv[i], "rescue")) flags |= LOADER_FLAGS_RESCUE; else if (!strcasecmp(argv[i], "nopass")) @@ -1795,7 +1799,11 @@ int main(int argc, char ** argv) { int testing = 0; int mediacheck = 0; char * virtpcon = NULL; + + struct ddlist *dd, *dditer; + poptContext optCon; + struct poptOption optionTable[] = { { "cmdline", '\0', POPT_ARG_STRING, &cmdLine, 0, NULL, NULL }, { "ksfile", '\0', POPT_ARG_STRING, &ksFile, 0, NULL, NULL }, @@ -1879,6 +1887,12 @@ int main(int argc, char ** argv) { flags |= LOADER_FLAGS_NOSHELL | LOADER_FLAGS_NOUSB; #endif + /* XXX if RHEL, enable the AUTODD feature by default, + * but we should come with more general way how to control this */ + if(!strncmp(getProductName(), "Red Hat", 7)){ + flags |= LOADER_FLAGS_AUTOMODDISK; + } + openLog(FL_TESTING(flags)); if (!FL_TESTING(flags)) openlog("loader", 0, LOG_LOCAL0); @@ -1939,6 +1953,22 @@ int main(int argc, char ** argv) { /* FIXME: this is a bit of a hack */ loaderData.modInfo = modInfo; + if(FL_AUTOMODDISK(flags)){ + logMessage(INFO, "Trying to detect vendor driver discs"); + dd = findDriverDiskByLabel(); + dditer = dd; + while(dditer){ + if(loadDriverDiskFromPartition(&loaderData, dditer->device)){ + logMessage(ERROR, "Automatic driver disk loader failed for %s.", dditer->device); + } + else{ + logMessage(INFO, "Automatic driver disk loader succeeded for %s.", dditer->device); + } + dditer = dditer->next; + } + ddlist_free(dd); + } + if (FL_MODDISK(flags)) { startNewt(); loadDriverDisks(DEVICE_ANY, &loaderData); @@ -2135,6 +2165,9 @@ int main(int argc, char ** argv) { tmparg++; } + if (FL_AUTOMODDISK(flags)) + *argptr++ = "--dlabel"; + if (FL_NOIPV4(flags)) *argptr++ = "--noipv4"; diff --git a/loader/loader.h b/loader/loader.h index a6e2d057c..b5252c32e 100644 --- a/loader/loader.h +++ b/loader/loader.h @@ -68,6 +68,7 @@ #define LOADER_FLAGS_IS_KICKSTART (((uint64_t) 1) << 35) #define LOADER_FLAGS_ALLOW_WIRELESS (((uint64_t) 1) << 36) #define LOADER_FLAGS_HAVE_CMSCONF (((uint64_t) 1) << 37) +#define LOADER_FLAGS_AUTOMODDISK (((uint64_t) 1) << 38) #define FL_TESTING(a) ((a) & LOADER_FLAGS_TESTING) #define FL_TEXT(a) ((a) & LOADER_FLAGS_TEXT) @@ -103,6 +104,7 @@ #define FL_IS_KICKSTART(a) ((a) & LOADER_FLAGS_IS_KICKSTART) #define FL_ALLOW_WIRELESS(a) ((a) & LOADER_FLAGS_ALLOW_WIRELESS) #define FL_HAVE_CMSCONF(a) ((a) & LOADER_FLAGS_HAVE_CMSCONF) +#define FL_AUTOMODDISK(a) ((a) & LOADER_FLAGS_AUTOMODDISK) void startNewt(void); void stopNewt(void); diff --git a/yuminstall.py b/yuminstall.py index 963303256..3bcb78d1f 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -592,7 +592,7 @@ class AnacondaYum(YumSorter): extraRepos = [] - if self.anaconda.id.extraModules: + if self.anaconda.id.extraModules or flags.dlabel: for d in glob.glob("/tmp/DD-*/rpms"): dirname = os.path.basename(os.path.dirname(d)) rid = "anaconda-%s" % dirname -- cgit From 283aa05cf327096dd332ce4c09756b6f64da2875 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 24 Mar 2009 08:55:54 -0400 Subject: Write the same arch to .discinfo as iutil.getArch() gives us (#490977). --- scripts/buildinstall | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/buildinstall b/scripts/buildinstall index ab8d2df49..71641ccb8 100755 --- a/scripts/buildinstall +++ b/scripts/buildinstall @@ -223,7 +223,7 @@ echo "Making images..." $MK_IMAGES $DEBUGSTR $NOGRSTR --imgdir $TREEDIR/install --arch $BASEARCH --product "$PRODUCTSTR" --version $VERSION --bugurl "$BUGURL" --output $OUTPUT $yumconf || die "image creation failed" echo "Writing .discinfo file" -$MK_STAMP --releasestr="$RELEASESTR" --arch=$BUILDARCH --discNum="ALL" --outfile=$OUTPUT/.discinfo +$MK_STAMP --releasestr="$RELEASESTR" --arch=$BASEARCH --discNum="ALL" --outfile=$OUTPUT/.discinfo rm -rf $TREEDIR $BUILDINSTDIR rm -f $yumconf -- cgit From 90006d07e51d962fbb2f062bb202c6027832f55d Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 24 Mar 2009 09:11:33 -0400 Subject: getDeviceByName does not expect the CD device to start with "/dev/" (#491768). --- anaconda | 3 +++ image.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/anaconda b/anaconda index 28893e29e..777970382 100755 --- a/anaconda +++ b/anaconda @@ -571,6 +571,9 @@ class Anaconda: if not tree.startswith("/"): tree = "/%s" %(tree,) + if device.startswith("/dev/"): + device = device[5:] + self.mediaDevice = device self.methodstr = "cdrom://%s" % tree else: diff --git a/image.py b/image.py index dea1e40d1..c77d92318 100644 --- a/image.py +++ b/image.py @@ -264,7 +264,7 @@ def scanForMedia(tree, storage): isys.umount(tree) continue - return dev.path + return dev.name return None -- cgit From 74df7604a2bfd0af0df3c60cffb4b5b0ab325cc8 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 24 Mar 2009 09:17:20 -0400 Subject: Add a format for ISO9660 filesystems. --- storage/formats/fs.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index d3b708278..f53dd992d 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -1028,6 +1028,23 @@ class NFSv4(NFS): register_device_format(NFSv4) + +class Iso9660FS(FS): + """ ISO9660 filesystem. """ + _type = "iso9660" + _formattable = False + _supported = True + _resizable = False + _bootable = False + _linuxNative = False + _dump = False + _check = False + _migratable = False + _defaultMountOptions = ["ro"] + +register_device_format(Iso9660FS) + + class NoDevFS(FS): """ nodev filesystem base class """ _type = "nodev" -- cgit From ed2883f06e20d2beda75e1f3fbdb5b827c31a9b0 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 24 Mar 2009 09:34:41 -0400 Subject: Use the mount and unmount methods on OpticalDevice.format now. --- image.py | 11 +++++------ yuminstall.py | 19 ++++++++++--------- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/image.py b/image.py index c77d92318..29ab10b40 100644 --- a/image.py +++ b/image.py @@ -255,8 +255,7 @@ def scanForMedia(tree, storage): continue try: - if isys.mount(dev.path, tree, fstype="iso9660", readOnly=True): - continue + dev.format.mount(mountpoint=tree) except: continue @@ -273,13 +272,13 @@ def umountImage(tree, currentMedia): isys.umount(tree, removeDir=False) isys.unlosetup("/dev/loop1") -def unmountCD(path, messageWindow): - if not path: +def unmountCD(dev, messageWindow): + if not dev: return while True: try: - isys.umount(path, removeDir=False) + dev.format.unmount() break except Exception, e: log.error("exception in _unmountCD: %s" %(e,)) @@ -288,7 +287,7 @@ def unmountCD(path, messageWindow): "Please make sure you're not accessing " "%s from the shell on tty2 " "and then click OK to retry.") - % (path,)) + % (dev.path,)) def verifyMedia(tree, discnum, timestamp=None): if os.access("%s/.discinfo" % tree, os.R_OK): diff --git a/yuminstall.py b/yuminstall.py index 3bcb78d1f..dc2dde597 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -322,27 +322,28 @@ class AnacondaYum(YumSorter): self._timestamp = f.readline().strip() f.close() + dev = self.anaconda.id.storage.devicetree.getDeviceByName(self.anaconda.mediaDevice) + dev.format.mountpoint = self.tree + # If self.currentMedia is None, then there shouldn't be anything # mounted. Before going further, see if the correct disc is already # in the drive. This saves a useless eject and insert if the user # has for some reason already put the disc in the drive. if self.currentMedia is None: try: - isys.mount(self.anaconda.mediaDevice, self.tree, - fstype="iso9660", readOnly=True) + dev.format.mount() if verifyMedia(self.tree, discnum, None): self.currentMedia = discnum return - isys.umount(self.tree) + dev.format.unmount() except: pass else: - unmountCD(self.tree, self.anaconda.intf.messageWindow) + unmountCD(dev, self.anaconda.intf.messageWindow) self.currentMedia = None - dev = self.anaconda.id.storage.devicetree.getDeviceByName(self.anaconda.mediaDevice) dev.eject() while True: @@ -354,8 +355,7 @@ class AnacondaYum(YumSorter): discnum)) try: - isys.mount(self.anaconda.mediaDevice, self.tree, - fstype = "iso9660", readOnly = True) + dev.format.mount() if verifyMedia(self.tree, discnum, self._timestamp): self.currentMedia = discnum @@ -364,8 +364,9 @@ class AnacondaYum(YumSorter): self.anaconda.intf.messageWindow(_("Wrong Disc"), _("That's not the correct %s disc.") % (productName,)) - isys.umount(self.tree) - isys.ejectCdrom(self.anaconda.mediaDevice) + + dev.format.unmount() + dev.eject() except: self.anaconda.intf.messageWindow(_("Error"), _("Unable to access the disc.")) -- cgit From 49933eafe930a8b55db4d1eb7fd9701d79332a3a Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 24 Mar 2009 10:55:47 -0400 Subject: Move protectedPartition setup into storageInitialize (#491781). --- instdata.py | 25 ------------------------- storage/__init__.py | 31 +++++++++++++++++++++++++++++-- 2 files changed, 29 insertions(+), 27 deletions(-) diff --git a/instdata.py b/instdata.py index e9a66245d..0b6df52f6 100644 --- a/instdata.py +++ b/instdata.py @@ -88,31 +88,6 @@ class InstallData: # XXX I still expect this to die when kickstart is the data store. self.ksdata = None - # We don't have install methods anymore, but put things that depend on - # the methodstr here. - if os.path.exists("/dev/live") and \ - stat.S_ISBLK(os.stat("/dev/live")[stat.ST_MODE]): - target = os.readlink("/dev/live") - self.storage.protectedPartitions = [target] - elif self.anaconda.methodstr and self.anaconda.methodstr.startswith("hd:"): - method = self.anaconda.methodstr[3:] - devspec = method.split(":", 3)[0] - - # XXX might as well move resolveDevice into DeviceTree - device = storage.resolveDevice(self.storage.devicetree, devspec) - if device is None: - if self.getUpgrade(): - return - else: - self.anaconda.intf.messageWindow(_("Unknown Device"), - _("The installation source given by device %s " - "could not be found. Please check your " - "parameters and try again.") % device, - type="custom", custom_buttons = [_("_Exit installer")]) - sys.exit(1) - - self.storage.protectedPartitions = [device.name] - def setInstallProgressClass(self, c): self.instProgress = c diff --git a/storage/__init__.py b/storage/__init__.py index 3c1394c73..9532f8830 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -54,14 +54,41 @@ import logging log = logging.getLogger("storage") def storageInitialize(anaconda): - anaconda.id.storage.shutdown() + storage = anaconda.id.storage + + storage.shutdown() if anaconda.dir == DISPATCH_BACK: return # XXX I don't understand why I have to do this udev_trigger(subsystem="block") - anaconda.id.storage.reset() + + # Set up the protected partitions list now. + if os.path.exists("/dev/live") and \ + stat.S_ISBLK(os.stat("/dev/live")[stat.ST_MODE]): + target = os.readlink("/dev/live") + storage.protectedPartitions = [target] + elif anaconda.methodstr and anaconda.methodstr.startswith("hd:"): + method = anaconda.methodstr[3:] + devspec = method.split(":", 3)[0] + + # XXX might as well move resolveDevice into DeviceTree + device = storage.resolveDevice(storage.devicetree, devspec) + if device is None: + if self.getUpgrade(): + return + else: + anaconda.intf.messageWindow(_("Unknown Device"), + _("The installation source given by device %s " + "could not be found. Please check your " + "parameters and try again.") % devspec, + type="custom", custom_buttons = [_("_Exit installer")]) + sys.exit(1) + + storage.protectedPartitions = [device.name] + + storage.reset() # dispatch.py helper function def storageComplete(anaconda): -- cgit From 1bc32876b6af2968460240054115f7097480ad00 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 24 Mar 2009 11:24:35 -0500 Subject: PReP formats can never be active. (#491865) --- storage/formats/prepboot.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/storage/formats/prepboot.py b/storage/formats/prepboot.py index 6d2174b33..fe0625f88 100644 --- a/storage/formats/prepboot.py +++ b/storage/formats/prepboot.py @@ -47,6 +47,10 @@ class PPCPRePBoot(DeviceFormat): """ DeviceFormat.__init__(self, *args, **kwargs) + @property + def status(self): + return False + register_device_format(PPCPRePBoot) -- cgit From 1392aa0641e2482056c5e89711a7b8065d49b133 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 24 Mar 2009 13:16:05 -0400 Subject: We don't even use partedUtils in this module. --- storage/iscsi.py | 1 - 1 file changed, 1 deletion(-) diff --git a/storage/iscsi.py b/storage/iscsi.py index 7c425174d..b9f975e26 100644 --- a/storage/iscsi.py +++ b/storage/iscsi.py @@ -31,7 +31,6 @@ import shutil import time import hashlib import random -import partedUtils log = logging.getLogger("anaconda") import gettext -- cgit From 04eb28ce07b81487c0271a9ff498a56a7b4895ee Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 24 Mar 2009 14:08:25 -0400 Subject: Move most of the parseFSTab logic into its own function. The purpose of doing this is so we can start to get a better handle on what line causes an error, so the user has some way to tell what they need to change in their /etc/fstab to make it work. --- storage/__init__.py | 141 ++++++++++++++++++++++++++++------------------------ upgrade.py | 9 ++-- 2 files changed, 81 insertions(+), 69 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 9532f8830..4f71125f6 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1213,6 +1213,77 @@ class FSSet(object): filesystems[device.format.mountpoint] = device return filesystems + def _parseOneLine(self, (devspec, mountpoint, fstype, options, dump, passno)): + # find device in the tree + device = resolveDevice(self.devicetree, + devspec, + cryptTab=self.cryptTab, + blkidTab=self.blkidTab) + if device: + # fall through to the bottom of this block + pass + elif devspec.startswith("/dev/loop"): + # FIXME: create devices.LoopDevice + log.warning("completely ignoring your loop mount") + elif ":" in devspec: + # NFS -- preserve but otherwise ignore + device = NFSDevice(devspec, + format=getFormat(fstype, + device=devspec), + exists=True) + elif devspec.startswith("/") and fstype == "swap": + # swap file + device = FileDevice(devspec, + parents=get_containing_device(devspec), + format=getFormat(fstype, + device=devspec, + exists=True), + exists=True) + elif fstype == "bind" or "bind" in options: + # bind mount... set fstype so later comparison won't + # turn up false positives + fstype = "bind" + device = FileDevice(devspec, + parents=get_containing_device(devspec), + exists=True) + device.format = getFormat("bind", + device=device.path, + exists=True) + elif mountpoint in ("/proc", "/sys", "/dev/shm", "/dev/pts"): + # drop these now -- we'll recreate later + return None + else: + # nodev filesystem -- preserve or drop completely? + format = getFormat(fstype) + if isinstance(format, get_device_format_class("nodev")): + device = NoDevice(format) + else: + device = Device(devspec) + + if device is None: + log.error("failed to resolve %s (%s) from fstab" % (devspec, + fstype)) + return None + + # make sure, if we're using a device from the tree, that + # the device's format we found matches what's in the fstab + fmt = getFormat(fstype, device=device.path) + if fmt.type != device.format.type: + log.warning("scanned format (%s) differs from fstab " + "format (%s)" % (device.format.type, fstype)) + + if device.format.mountable: + device.format.mountpoint = mountpoint + device.format.mountopts = options + + # is this useful? + try: + device.format.options = options + except AttributeError: + pass + + return device + def parseFSTab(self, chroot=""): """ parse /etc/fstab @@ -1276,74 +1347,14 @@ class FSSet(object): (devspec, mountpoint, fstype, options, dump, passno) = fields - # find device in the tree - device = resolveDevice(self.devicetree, - devspec, - cryptTab=cryptTab, - blkidTab=blkidTab) - if device: - # fall through to the bottom of this block - pass - elif devspec.startswith("/dev/loop"): - # FIXME: create devices.LoopDevice - log.warning("completely ignoring your loop mount") - elif ":" in devspec: - # NFS -- preserve but otherwise ignore - device = NFSDevice(devspec, - format=getFormat(fstype, - device=devspec), - exists=True) - elif devspec.startswith("/") and fstype == "swap": - # swap file - device = FileDevice(devspec, - parents=get_containing_device(devspec), - format=getFormat(fstype, - device=devspec, - exists=True), - exists=True) - elif fstype == "bind" or "bind" in options: - # bind mount... set fstype so later comparison won't - # turn up false positives - fstype = "bind" - device = FileDevice(devspec, - parents=get_containing_device(devspec), - exists=True) - device.format = getFormat("bind", - device=device.path, - exists=True) - elif mountpoint in ("/proc", "/sys", "/dev/shm", "/dev/pts"): - # drop these now -- we'll recreate later - continue - else: - # nodev filesystem -- preserve or drop completely? - format = getFormat(fstype) - if isinstance(format, get_device_format_class("nodev")): - device = NoDevice(format) - else: - device = Device(devspec) + try: + device = self._parseOneLine((devspec, mountpoint, fstype, options, dump, passno)) + except Exception as e: + raise Exception("fstab entry %s is malformed: %s" % (devspec, e)) - if device is None: - log.error("failed to resolve %s (%s) from fstab" % (devspec, - fstype)) + if not device: continue - # make sure, if we're using a device from the tree, that - # the device's format we found matches what's in the fstab - fmt = getFormat(fstype, device=device.path) - if fmt.type != device.format.type: - log.warning("scanned format (%s) differs from fstab " - "format (%s)" % (device.format.type, fstype)) - - if device.format.mountable: - device.format.mountpoint = mountpoint - device.format.mountopts = options - - # is this useful? - try: - device.format.options = options - except AttributeError: - pass - if device not in self.devicetree.devices.values(): self.devicetree._addDevice(device) diff --git a/upgrade.py b/upgrade.py index c080c8651..be701fb24 100644 --- a/upgrade.py +++ b/upgrade.py @@ -234,11 +234,12 @@ def upgradeMountFilesystems(anaconda): mountExistingSystem(anaconda, anaconda.id.upgradeRoot[0], allowDirty = 0) - except Exception: + except ValueError as e: + log.error("Error mounting filesystem: %s" % e) anaconda.intf.messageWindow(_("Mount failed"), - _("One or more of the file systems listed in the " - "/etc/fstab on your Linux system cannot be mounted. " - "Please fix this problem and try to upgrade again.")) + _("The following error occurred when mounting the file " + "systems listed in /etc/fstab. Please fix this problem " + "and try to upgrade again.\n%s" % e)) sys.exit(0) checkLinks = ( '/etc', '/var', '/var/lib', '/var/lib/rpm', -- cgit From 9f00b3afb2ee24eb375787c9d44ae806fbddbabc Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 24 Mar 2009 16:49:18 -0400 Subject: Move resolveDevice into the DeviceTree class. --- storage/__init__.py | 88 ++++----------------------------------------------- storage/devicetree.py | 71 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 75 insertions(+), 84 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 4f71125f6..afb55f95e 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -73,8 +73,7 @@ def storageInitialize(anaconda): method = anaconda.methodstr[3:] devspec = method.split(":", 3)[0] - # XXX might as well move resolveDevice into DeviceTree - device = storage.resolveDevice(storage.devicetree, devspec) + device = storage.devicetree.resolveDevice(devspec) if device is None: if self.getUpgrade(): return @@ -1076,9 +1075,8 @@ class CryptTab(object): (name, devspec, keyfile, options) = fields # resolve devspec to a device in the tree - device = resolveDevice(self.devicetree, - devspec, - blkidTab=self.blkidTab) + device = self.devicetree.resolveDevice(devspec, + blkidTab=self.blkidTab) if device: self.mappings[name] = {"device": device, "keyfile": keyfile, @@ -1215,10 +1213,9 @@ class FSSet(object): def _parseOneLine(self, (devspec, mountpoint, fstype, options, dump, passno)): # find device in the tree - device = resolveDevice(self.devicetree, - devspec, - cryptTab=self.cryptTab, - blkidTab=self.blkidTab) + device = self.devicetree.resolveDevice(devspec, + cryptTab=self.cryptTab, + blkidTab=self.blkidTab) if device: # fall through to the bottom of this block pass @@ -1722,76 +1719,3 @@ class FSSet(object): fstab = fstab + format % (devspec, mountpoint, fstype, options, dump, passno) return fstab - -def resolveDevice(tree, devspec, blkidTab=None, cryptTab=None): - # find device in the tree - device = None - if devspec.startswith("UUID="): - # device-by-uuid - uuid = devspec.partition("=")[2] - device = tree.uuids.get(uuid) - if device is None: - log.error("failed to resolve device %s" % devspec) - elif devspec.startswith("LABEL="): - # device-by-label - label = devspec.partition("=")[2] - device = tree.fslabels.get(label) - if device is None: - log.error("failed to resolve device %s" % devspec) - elif devspec.startswith("/dev/"): - # device path - device = tree.devices.get(devspec) - if device is None: - if blkidTab: - # try to use the blkid.tab to correlate the device - # path with a UUID - blkidTabEnt = blkidTab.get(devspec) - if blkidTabEnt: - log.debug("found blkid.tab entry for '%s'" % devspec) - uuid = blkidTabEnt.get("UUID") - if uuid: - device = tree.getDeviceByUuid(uuid) - if device: - devstr = device.name - else: - devstr = "None" - log.debug("found device '%s' in tree" % devstr) - if device and device.format and \ - device.format.type == "luks": - map_name = device.format.mapName - log.debug("luks device; map name is '%s'" % map_name) - mapped_dev = tree.getDeviceByName(map_name) - if mapped_dev: - device = mapped_dev - - if device is None and cryptTab and \ - devspec.startswith("/dev/mapper/"): - # try to use a dm-crypt mapping name to - # obtain the underlying device, possibly - # using blkid.tab - cryptTabEnt = cryptTab.get(devspec.split("/")[-1]) - if cryptTabEnt: - luks_dev = cryptTabEnt['device'] - try: - device = tree.getChildren(luks_dev)[0] - except IndexError as e: - pass - elif device is None: - # dear lvm: can we please have a few more device nodes - # for each logical volume? - # three just doesn't seem like enough. - name = devspec[5:] # strip off leading "/dev/" - (vg_name, slash, lv_name) = name.partition("/") - if lv_name and not "/" in lv_name: - # looks like we may have one - lv = "%s-%s" % (vg_name, lv_name) - device = tree.getDeviceByName(lv) - - if device: - log.debug("resolved '%s' to '%s' (%s)" % (devspec, device.name, device.type)) - else: - log.debug("failed to resolve '%s'" % devspec) - return device - - - diff --git a/storage/devicetree.py b/storage/devicetree.py index 63339dd05..d8cbbc195 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1630,5 +1630,72 @@ class DeviceTree(object): """ Return a list of a device's children. """ return [c for c in self._devices if device in c.parents] - - + def resolveDevice(self, devspec, blkidTab=None, cryptTab=None): + # find device in the tree + device = None + if devspec.startswith("UUID="): + # device-by-uuid + uuid = devspec.partition("=")[2] + device = self.uuids.get(uuid) + if device is None: + log.error("failed to resolve device %s" % devspec) + elif devspec.startswith("LABEL="): + # device-by-label + label = devspec.partition("=")[2] + device = self.fslabels.get(label) + if device is None: + log.error("failed to resolve device %s" % devspec) + elif devspec.startswith("/dev/"): + # device path + device = self.devices.get(devspec) + if device is None: + if blkidTab: + # try to use the blkid.tab to correlate the device + # path with a UUID + blkidTabEnt = blkidTab.get(devspec) + if blkidTabEnt: + log.debug("found blkid.tab entry for '%s'" % devspec) + uuid = blkidTabEnt.get("UUID") + if uuid: + device = self.getDeviceByUuid(uuid) + if device: + devstr = device.name + else: + devstr = "None" + log.debug("found device '%s' in tree" % devstr) + if device and device.format and \ + device.format.type == "luks": + map_name = device.format.mapName + log.debug("luks device; map name is '%s'" % map_name) + mapped_dev = self.getDeviceByName(map_name) + if mapped_dev: + device = mapped_dev + + if device is None and cryptTab and \ + devspec.startswith("/dev/mapper/"): + # try to use a dm-crypt mapping name to + # obtain the underlying device, possibly + # using blkid.tab + cryptTabEnt = cryptTab.get(devspec.split("/")[-1]) + if cryptTabEnt: + luks_dev = cryptTabEnt['device'] + try: + device = self.getChildren(luks_dev)[0] + except IndexError as e: + pass + elif device is None: + # dear lvm: can we please have a few more device nodes + # for each logical volume? + # three just doesn't seem like enough. + name = devspec[5:] # strip off leading "/dev/" + (vg_name, slash, lv_name) = name.partition("/") + if lv_name and not "/" in lv_name: + # looks like we may have one + lv = "%s-%s" % (vg_name, lv_name) + device = self.getDeviceByName(lv) + + if device: + log.debug("resolved '%s' to '%s' (%s)" % (devspec, device.name, device.type)) + else: + log.debug("failed to resolve '%s'" % devspec) + return device -- cgit From a1944ccfe79122de89523fb4012a1af1f3154d27 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 24 Mar 2009 14:14:59 -0400 Subject: Override _setDevice and _getDevice in NFS. The base class expects a device to start with "/dev/", which obviously an NFS device does not. --- storage/formats/fs.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index f53dd992d..904bcfceb 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -1018,6 +1018,17 @@ class NFS(FS): @property def mountable(self): return False + + def _setDevice(self, devspec): + self._deviceCheck(devspec) + self._device = devspec + + def _getDevice(self): + return self._device + + device = property(lambda f: f._getDevice(), + lambda f,d: f._setDevice(d), + doc="Full path the device this format occupies") register_device_format(NFS) -- cgit From de4b0d629f1db020dc8a3e66fcd17c61a5b48fae Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 24 Mar 2009 14:19:03 -0400 Subject: NFSDevice does not take exists= as a parameter. --- storage/__init__.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index afb55f95e..e9d0b7874 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1226,8 +1226,7 @@ class FSSet(object): # NFS -- preserve but otherwise ignore device = NFSDevice(devspec, format=getFormat(fstype, - device=devspec), - exists=True) + device=devspec)) elif devspec.startswith("/") and fstype == "swap": # swap file device = FileDevice(devspec, -- cgit From 19a7393743e9da1d1bc523862632b62b1964b9df Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 24 Mar 2009 14:20:32 -0400 Subject: fslabels -> labels. --- storage/devicetree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index d8cbbc195..b1ecaa67d 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1642,7 +1642,7 @@ class DeviceTree(object): elif devspec.startswith("LABEL="): # device-by-label label = devspec.partition("=")[2] - device = self.fslabels.get(label) + device = self.labels.get(label) if device is None: log.error("failed to resolve device %s" % devspec) elif devspec.startswith("/dev/"): -- cgit From ad725c2140eac1434c78bb3ac6cd3f9a9a81ecb5 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Tue, 24 Mar 2009 14:50:31 -0400 Subject: Register the NoDevFS class. --- storage/formats/fs.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 904bcfceb..935065125 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -1068,6 +1068,8 @@ class NoDevFS(FS): def _setDevice(self, devspec): self._device = devspec +register_device_format(NoDevFS) + class DevPtsFS(NoDevFS): """ devpts filesystem. """ -- cgit From b39f630954627f628ecfe6fdbdc3a48707f35fc1 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 24 Mar 2009 14:07:17 -0500 Subject: Put line breaks in between crypttab entries. (#491938) --- storage/__init__.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index e9d0b7874..fd9f28996 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1109,10 +1109,10 @@ class CryptTab(object): crypttab = "" for name in self.mappings: entry = self[name] - crypttab += "%s UUID=%s %s %s" % (name, - entry['device'].format.uuid, - entry['keyfile'], - entry['options']) + crypttab += "%s UUID=%s %s %s\n" % (name, + entry['device'].format.uuid, + entry['keyfile'], + entry['options']) return crypttab def __getitem__(self, key): -- cgit From db03ead33b9c59551c4072a933cac850c0995103 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 24 Mar 2009 13:51:10 -1000 Subject: Revert "Port the dlabel feature from RHEL (#489314)" This reverts commit 16632332b3a49994edf0016a3c5c3200d32cf6ac. This patch calls doMultiMount(), which no longer exists. It also references ddFsTypes, which looks to not exist either. --- anaconda | 4 -- flags.py | 1 - loader/Makefile | 2 +- loader/driverdisk.c | 104 ---------------------------------------------------- loader/driverdisk.h | 12 ------ loader/loader.c | 33 ----------------- loader/loader.h | 2 - yuminstall.py | 2 +- 8 files changed, 2 insertions(+), 158 deletions(-) diff --git a/anaconda b/anaconda index 777970382..487795f26 100755 --- a/anaconda +++ b/anaconda @@ -251,7 +251,6 @@ def parseOptions(): op.add_option("--nomount", dest="rescue_nomount", action="store_true", default=False) op.add_option("--updates", dest="updateSrc", action="store", type="string") op.add_option("--dogtail", dest="dogtail", action="store", type="string") - op.add_option("--dlabel", action="store_true", default=False) return op.parse_args() @@ -677,9 +676,6 @@ if __name__ == "__main__": # Default is to prompt to mount the installed system. anaconda.rescue_mount = not opts.rescue_nomount - if opts.dlabel: #autodetected driverdisc in use - flags.dlabel = True - if opts.noipv4: flags.useIPv4 = False diff --git a/flags.py b/flags.py index 5ee0a9fe1..8dbcc9f9d 100644 --- a/flags.py +++ b/flags.py @@ -70,7 +70,6 @@ class Flags: self.__dict__['flags']['test'] = 0 self.__dict__['flags']['rootpath'] = 0 self.__dict__['flags']['livecdInstall'] = 0 - self.__dict__['flags']['dlabel'] = 0 self.__dict__['flags']['ibft'] = 1 self.__dict__['flags']['iscsi'] = 0 self.__dict__['flags']['serial'] = 0 diff --git a/loader/Makefile b/loader/Makefile index cbdf03777..def2badea 100644 --- a/loader/Makefile +++ b/loader/Makefile @@ -26,7 +26,7 @@ else TARGET=depend $(PROGS) endif -LIBS = -lnewt -lslang -lz -lpopt ../isys/libisys.a -lcheckisomd5 -liscsi -lblkid -luuid +LIBS = -lnewt -lslang -lz -lpopt ../isys/libisys.a -lcheckisomd5 -liscsi # devmapper LIBS += $(shell pkg-config --libs devmapper) diff --git a/loader/driverdisk.c b/loader/driverdisk.c index 948ab9eec..9e7a30dd2 100644 --- a/loader/driverdisk.c +++ b/loader/driverdisk.c @@ -48,8 +48,6 @@ #include "nfsinstall.h" #include "urlinstall.h" -#include - #include "../isys/isys.h" #include "../isys/imount.h" #include "../isys/eddsupport.h" @@ -512,108 +510,6 @@ static void loadFromLocation(struct loaderData_s * loaderData, char * dir) { busProbe(0); } -/* - * Utility functions to maintain linked-list of device names - * */ - -struct ddlist* ddlist_add(struct ddlist *list, const char* device) -{ - struct ddlist* item; - - item = (struct ddlist*)malloc(sizeof(struct ddlist)); - if(item==NULL){ - return list; - } - - item->device = strdup(device); - item->next = list; - - return item; -} - -int ddlist_free(struct ddlist *list) -{ - struct ddlist *next; - int count = 0; - - while(list!=NULL){ - next = list->next; - free(list->device); - free(list); - list = next; - count++; - } - - return count; -} - - -/* - * Look for partition with specific label (part of #316481) - */ -struct ddlist* findDriverDiskByLabel(void) -{ - char *ddLabel = "OEMDRV"; - struct ddlist *ddDevice = NULL; - blkid_cache bCache; - - int res; - blkid_dev_iterate bIter; - blkid_dev bDev; - - if(blkid_get_cache(&bCache, NULL)<0){ - logMessage(ERROR, _("Cannot initialize cache instance for blkid")); - return NULL; - } - if((res = blkid_probe_all(bCache))<0){ - logMessage(ERROR, _("Cannot probe devices in blkid: %d"), res); - return NULL; - } - - bIter = blkid_dev_iterate_begin(bCache); - blkid_dev_set_search(bIter, "LABEL", ddLabel); - while((res = blkid_dev_next(bIter, &bDev))!=0){ - bDev = blkid_verify(bCache, bDev); - if(!bDev) - continue; - logMessage(DEBUGLVL, _("Adding driver disc %s to the list of available DDs."), blkid_dev_devname(bDev)); - ddDevice = ddlist_add(ddDevice, blkid_dev_devname(bDev)); - /*blkid_free_dev(bDev); -- probably taken care of by the put cache call.. it is not exposed in the API */ - } - blkid_dev_iterate_end(bIter); - - blkid_put_cache(bCache); - - return ddDevice; -} - -int loadDriverDiskFromPartition(struct loaderData_s *loaderData, char* device) -{ - int rc; - - logMessage(INFO, "trying to mount %s", device); - if (doMultiMount(device, "/tmp/drivers", ddFsTypes, "ro", NULL)) { - logMessage(ERROR, _("Failed to mount driver disk.")); - return -1; - } - - rc = verifyDriverDisk("/tmp/drivers"); - if (rc == LOADER_BACK) { - logMessage(ERROR, _("Driver disk is invalid for this " - "release of %s."), getProductName()); - umount("/tmp/drivers"); - return -2; - } - - rc = loadDriverDisk(loaderData, "/tmp/drivers"); - umount("/tmp/drivers"); - if (rc == LOADER_BACK) { - return -3; - } - - return 0; -} - void getDDFromSource(struct loaderData_s * loaderData, char * src) { char *path = "/tmp/dd.img"; int unlinkf = 0; diff --git a/loader/driverdisk.h b/loader/driverdisk.h index 0e88ca4ac..e6e919dd2 100644 --- a/loader/driverdisk.h +++ b/loader/driverdisk.h @@ -39,16 +39,4 @@ void useKickstartDD(struct loaderData_s * loaderData, int argc, void getDDFromSource(struct loaderData_s * loaderData, char * src); -int loadDriverDiskFromPartition(struct loaderData_s *loaderData, char* device); - -struct ddlist { - char* device; - struct ddlist* next; -}; - -struct ddlist* ddlist_add(struct ddlist *list, const char* device); -int ddlist_free(struct ddlist *list); - -struct ddlist* findDriverDiskByLabel(void); - #endif diff --git a/loader/loader.c b/loader/loader.c index 178f9250b..98ea24cba 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -930,10 +930,6 @@ static void parseCmdLineFlags(struct loaderData_s * loaderData, else if (!strcasecmp(argv[i], "dd") || !strcasecmp(argv[i], "driverdisk")) flags |= LOADER_FLAGS_MODDISK; - else if (!strcasecmp(argv[i], "dlabel=on")) - flags |= LOADER_FLAGS_AUTOMODDISK; - else if (!strcasecmp(argv[i], "dlabel=off")) - flags &= ~LOADER_FLAGS_AUTOMODDISK; else if (!strcasecmp(argv[i], "rescue")) flags |= LOADER_FLAGS_RESCUE; else if (!strcasecmp(argv[i], "nopass")) @@ -1799,11 +1795,7 @@ int main(int argc, char ** argv) { int testing = 0; int mediacheck = 0; char * virtpcon = NULL; - - struct ddlist *dd, *dditer; - poptContext optCon; - struct poptOption optionTable[] = { { "cmdline", '\0', POPT_ARG_STRING, &cmdLine, 0, NULL, NULL }, { "ksfile", '\0', POPT_ARG_STRING, &ksFile, 0, NULL, NULL }, @@ -1887,12 +1879,6 @@ int main(int argc, char ** argv) { flags |= LOADER_FLAGS_NOSHELL | LOADER_FLAGS_NOUSB; #endif - /* XXX if RHEL, enable the AUTODD feature by default, - * but we should come with more general way how to control this */ - if(!strncmp(getProductName(), "Red Hat", 7)){ - flags |= LOADER_FLAGS_AUTOMODDISK; - } - openLog(FL_TESTING(flags)); if (!FL_TESTING(flags)) openlog("loader", 0, LOG_LOCAL0); @@ -1953,22 +1939,6 @@ int main(int argc, char ** argv) { /* FIXME: this is a bit of a hack */ loaderData.modInfo = modInfo; - if(FL_AUTOMODDISK(flags)){ - logMessage(INFO, "Trying to detect vendor driver discs"); - dd = findDriverDiskByLabel(); - dditer = dd; - while(dditer){ - if(loadDriverDiskFromPartition(&loaderData, dditer->device)){ - logMessage(ERROR, "Automatic driver disk loader failed for %s.", dditer->device); - } - else{ - logMessage(INFO, "Automatic driver disk loader succeeded for %s.", dditer->device); - } - dditer = dditer->next; - } - ddlist_free(dd); - } - if (FL_MODDISK(flags)) { startNewt(); loadDriverDisks(DEVICE_ANY, &loaderData); @@ -2165,9 +2135,6 @@ int main(int argc, char ** argv) { tmparg++; } - if (FL_AUTOMODDISK(flags)) - *argptr++ = "--dlabel"; - if (FL_NOIPV4(flags)) *argptr++ = "--noipv4"; diff --git a/loader/loader.h b/loader/loader.h index b5252c32e..a6e2d057c 100644 --- a/loader/loader.h +++ b/loader/loader.h @@ -68,7 +68,6 @@ #define LOADER_FLAGS_IS_KICKSTART (((uint64_t) 1) << 35) #define LOADER_FLAGS_ALLOW_WIRELESS (((uint64_t) 1) << 36) #define LOADER_FLAGS_HAVE_CMSCONF (((uint64_t) 1) << 37) -#define LOADER_FLAGS_AUTOMODDISK (((uint64_t) 1) << 38) #define FL_TESTING(a) ((a) & LOADER_FLAGS_TESTING) #define FL_TEXT(a) ((a) & LOADER_FLAGS_TEXT) @@ -104,7 +103,6 @@ #define FL_IS_KICKSTART(a) ((a) & LOADER_FLAGS_IS_KICKSTART) #define FL_ALLOW_WIRELESS(a) ((a) & LOADER_FLAGS_ALLOW_WIRELESS) #define FL_HAVE_CMSCONF(a) ((a) & LOADER_FLAGS_HAVE_CMSCONF) -#define FL_AUTOMODDISK(a) ((a) & LOADER_FLAGS_AUTOMODDISK) void startNewt(void); void stopNewt(void); diff --git a/yuminstall.py b/yuminstall.py index dc2dde597..b91172ac8 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -593,7 +593,7 @@ class AnacondaYum(YumSorter): extraRepos = [] - if self.anaconda.id.extraModules or flags.dlabel: + if self.anaconda.id.extraModules: for d in glob.glob("/tmp/DD-*/rpms"): dirname = os.path.basename(os.path.dirname(d)) rid = "anaconda-%s" % dirname -- cgit From eca5079df56d8a202da5b9e83686d84561393f06 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 24 Mar 2009 16:23:54 -0500 Subject: Use the same units (MB) for extent size that we do for everything else. --- storage/devicelibs/lvm.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py index aa930718b..52d9315a5 100644 --- a/storage/devicelibs/lvm.py +++ b/storage/devicelibs/lvm.py @@ -238,7 +238,7 @@ def pvinfo(device): def vgcreate(vg_name, pv_list, pe_size): argv = ["vgcreate"] if pe_size: - argv.extend(["-s", "%dM" % pe_size]) + argv.extend(["-s", "%dm" % pe_size]) argv.extend(config_args) argv.append(vg_name) argv.extend(pv_list) -- cgit From 52f15f952e6e7686905931c43aa4d1fcfa362c3e Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 24 Mar 2009 17:58:16 -0500 Subject: Work around a bug in mdadm incremental assembly. We write out a temporary mdadm.conf so that mdadm doesn't assign preexisting arrays bizarre minor numbers like 126. --- storage/devices.py | 11 +++++++++-- storage/devicetree.py | 4 ++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index 064ebc14c..ce23d7d27 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -2029,6 +2029,9 @@ class MDRaidArrayDevice(StorageDevice): raise DeviceError("cannot find class for 'mdmember'") #self.probe() + if self.exists and self.uuid: + open("/etc/mdadm.conf", "a").write("ARRAY %s UUID=%s\n" + % (self.path, self.uuid)) @property def size(self): @@ -2163,9 +2166,13 @@ class MDRaidArrayDevice(StorageDevice): self.devices.append(device) device.addChild() - if self.status: - device.setup() + device.setup() + udev_settle(timeout=10) + try: mdraid.mdadd(device.path) + except MDRaidError as e: + log.warning("failed to add member %s to md array %s: %s" + % (device.path, self.path, e)) def _removeDevice(self, device): """ Remove a component device from the array. diff --git a/storage/devicetree.py b/storage/devicetree.py index b1ecaa67d..93ac747c6 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1491,6 +1491,10 @@ class DeviceTree(object): self._handleInconsistencies(leaf) self.teardownAll() + try: + os.unlink("/etc/mdadm.conf") + except OSError: + log.info("failed to unlink /etc/mdadm.conf") def teardownAll(self): """ Run teardown methods on all devices. """ -- cgit From 82c16d2ca057417b6e02bb389b26005ea50477f4 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 24 Mar 2009 18:00:25 -0500 Subject: Check that partition is on the disk before trying to remove it. (#491997) --- storage/partitioning.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/storage/partitioning.py b/storage/partitioning.py index 95d6ec28d..f71665734 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -606,6 +606,9 @@ def allocatePartitions(disks, partitions): #_part.disk.partedDisk.removePartition(_part.partedPartition) partedDisk = partedDisks[_part.disk.partedDisk.device.path] #log.debug("removing part %s (%s) from disk %s (%s)" % (_part.partedPartition.path, [p.path for p in _part.partedPartition.disk.partitions], partedDisk.device.path, [p.path for p in partedDisk.partitions])) + if not partedDisk.getPartitionByPath(_part.path): + continue + partedDisk.removePartition(_part.partedPartition) # remove empty extended so it doesn't interfere extended = partedDisk.getExtendedPartition() -- cgit From 9eff413553721203b7ff811175c4ca1e2f64ab38 Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 24 Mar 2009 18:02:39 -0500 Subject: Be a little bit smarter about allocating space to grow parts. (#491761) parted.disk.Disk.maximizePartition leads to libparted applying a cylinder-alignment constraint, which we would frikking ask for if we wanted it. So, for now, we can use this to identify chunks of free space that will initially be allocated to requests whose max size will prevent them from making full use of their share of the space. Cases where this comes into play are ones where an initially larger request has a relatively low max size and an initially smaller request has no max size at all. Since we base the allocation of free space for growing on the requests' initial size, we could previously end up with very large chunks of unused free space. --- storage/partitioning.py | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/storage/partitioning.py b/storage/partitioning.py index f71665734..9f42a7299 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -834,12 +834,42 @@ def growPartitions(disks, partitions): part.partedPartition.geometry.length)) log.debug("growable total is now %d sectors" % disk_total) + # now we loop through the partitions... + # this first loop is to identify obvious chunks of free space that + # will be left over due to max size + leftover = 0 + limited = {} + unlimited_total = 0 + for part in growable: + # calculate max number of sectors this request can grow + req_sectors = part.partedPartition.geometry.length + share = float(req_sectors) / float(disk_total) + max_grow = (share * disk_free) + max_sectors = req_sectors + max_grow + limited[part.name] = False + + if part.req_max_size: + req_max_sect = (part.req_max_size * (1024 * 1024)) / sectorSize + if req_max_sect < max_sectors: + mb = ((max_sectors - req_max_sect) * sectorSize) / (1024*1024) + + log.debug("adding %dMB to leftovers from %s" + % (mb, part.name)) + leftover += (max_sectors - req_max_sect) + limited[part.name] = True + + if not limited[part.name]: + unlimited_total += req_sectors + # now we loop through the partitions... for part in growable: # calculate max number of sectors this request can grow req_sectors = part.partedPartition.geometry.length share = float(req_sectors) / float(disk_total) max_grow = (share * disk_free) + if not limited[part.name]: + leftover_share = float(req_sectors) / float(unlimited_total) + max_grow += leftover_share * leftover max_sectors = req_sectors + max_grow max_mb = (max_sectors * sectorSize) / (1024 * 1024) log.debug("%s: base_size=%dMB, max_size=%sMB" % (part.name, -- cgit From 0bb7d5413f78f0de18d25f1afb3f422f29dd7f49 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Tue, 24 Mar 2009 21:06:23 +0100 Subject: Call udev_settle after committing changes to a disk (#491529) --- storage/devices.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/storage/devices.py b/storage/devices.py index ce23d7d27..f8aaac2d2 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -795,6 +795,9 @@ class DiskDevice(StorageDevice): if keepTrying: raise DeviceError("cannot commit to disk %s after %d attempts" % (self.name, maxTries,)) + # commit makes the kernel re-scan the partition table + udev_settle() + def destroy(self): """ Destroy the device. """ log_method_call(self, self.name, status=self.status) -- cgit From 8807c70fb2d459669366308c52aa7c1f4eb9fbda Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 24 Mar 2009 19:20:56 -0500 Subject: Get the UUID of each md array we create. (#491796) --- storage/devices.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/storage/devices.py b/storage/devices.py index f8aaac2d2..2f1319e34 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -107,7 +107,7 @@ import platform from errors import * from iutil import log_method_call, notify_kernel, numeric_type -from udev import udev_settle +from udev import * from formats import get_device_format_class, getFormat import gettext @@ -2287,8 +2287,9 @@ class MDRaidArrayDevice(StorageDevice): # the array is automatically activated upon creation, but... self.setup() udev_settle() - - # FIXME: we need to find our UUID now that the array exists + self.updateSysfsPath() + info = udev_get_block_device("/sys%s" % self.sysfsPath) + self.uuid = udev_device_get_md_uuid(info) @property def formatArgs(self): -- cgit From 01d71b73beb0805b1b28bf1a9a776e317a68148a Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Mon, 23 Mar 2009 13:09:40 -1000 Subject: Build new _isys.so for updates.img if needed. If _isys.so module code has changed, build a new module and copy it in to updates.img. Also fix a problem with the KEEP=y variable. --- Makefile | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Makefile b/Makefile index d5fa03241..f7dff9a6c 100644 --- a/Makefile +++ b/Makefile @@ -183,6 +183,7 @@ updates: @if [ ! -d updates-img ]; then \ mkdir updates-img ; \ fi ; \ + build_isys="$$(git diff --stat anaconda-11.5.0.35-1 isys | grep " | " | cut -d ' ' -f 2 | egrep "(Makefile|\.h|\.c)$$")" ; \ git diff --stat $(ARCHIVE_TAG) | grep " | " | \ grep -v "\.spec" | grep -v "Makefile" | grep -v "\.c\ " | \ grep -v "\.h" | grep -v "\.sh" | \ @@ -199,11 +200,15 @@ updates: cp -a $$sourcefile updates-img ;; \ esac ; \ done ; \ + if [ ! -z "$$build_isys" ]; then \ + make -C isys ; \ + cp isys/_isys.so updates-img ; \ + fi ; \ cd updates-img ; \ echo -n "Creating updates.img..." ; \ ( find . | cpio -c -o | gzip -9c ) > ../updates.img ; \ cd .. ; \ keep="$$(echo $(KEEP) | cut -c1 | tr [a-z] [A-Z])" ; \ if [ ! "$$keep" = "Y" ]; then \ - rm -rf updates-imgs ; \ + rm -rf updates-img ; \ fi -- cgit From 7603bfdd0838320328ed73c00dc278676803412c Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 24 Mar 2009 11:56:16 -1000 Subject: Remove unnecessary istruefalse() function. --- iw/partition_ui_helpers_gui.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index fb311df44..e6324f662 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -35,11 +35,6 @@ from storage.formats import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) -def istruefalse(val): - if val is None or not val: - return False - return True - class WideCheckList(checklist.CheckList): def toggled_item(self, data, row): @@ -318,7 +313,7 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, formatcb = gtk.CheckButton(label=_("_Format as:")) maintable.attach(formatcb, 0, 1, row, row + 1) - formatcb.set_active(istruefalse(not origfs.exists)) + formatcb.set_active(not origfs.exists) rc["formatcb"] = formatcb fstypeCombo = createFSTypeMenu(origfs, fstypechangeCB, @@ -336,7 +331,7 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, if origfs.migratable: migratecb = gtk.CheckButton(label=_("Mi_grate filesystem to:")) - migratecb.set_active(istruefalse(origfs.migrate)) + migratecb.set_active(origfs.migrate) migtypes = [origfs.migrationTarget] -- cgit From 8dc2ccdf97adb9a9b16bf54dd27dd5ad7829d81c Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 24 Mar 2009 11:57:52 -1000 Subject: Remove unused noformatCB() function. --- iw/partition_ui_helpers_gui.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index e6324f662..4c273f2c2 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -279,21 +279,13 @@ def formatMigrateOptionCB(widget, data): setMntPtComboStateFromType(ofstype, mntptcombo) -def noformatCB(widget, data): - (combowidget, mntptcombo, ofstype) = data - combowidget.set_sensitive(not widget.get_active()) - - # inject event for fstype menu - if widget.get_active(): - setMntPtComboStateFromType(ofstype, mntptcombo) - """ createPreExistFSOptionSection: given inputs for a preexisting partition, create a section that will provide format and migrate options Returns the value of row after packing into the maintable, and a dictionary consistenting of: - noformatcb - checkbutton for 'format as new fs' + formatcb - checkbutton for 'format as new fs' fstype - part of format fstype menu fstypeMenu - part of format fstype menu migratecb - checkbutton for migrate fs -- cgit From 85a531e65750e3b35995b4944f97d4455387018b Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 24 Mar 2009 12:11:59 -1000 Subject: Do not flag every existing partition for resize (#491803) Check the resizable property as well as targetSize not being zero (indicates no new targetSize has been set) and finally compare targetSize to currentSize. --- iw/partition_ui_helpers_gui.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/iw/partition_ui_helpers_gui.py b/iw/partition_ui_helpers_gui.py index 4c273f2c2..9d7f7c556 100644 --- a/iw/partition_ui_helpers_gui.py +++ b/iw/partition_ui_helpers_gui.py @@ -349,7 +349,9 @@ def createPreExistFSOptionSection(origrequest, maintable, row, mountCombo, if origrequest.resizable: resizecb = gtk.CheckButton(label=_("_Resize")) - resizecb.set_active(origrequest.targetSize != origrequest.currentSize) + resizecb.set_active(origrequest.resizable and \ + ((origrequest.targetSize != 0) and \ + (origrequest.targetSize != origrequest.currentSize))) rc["resizecb"] = resizecb maintable.attach(resizecb, 0, 1, row, row + 1) -- cgit From 60bb9f995797df65b1333560cf226118639cc3e9 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 24 Mar 2009 14:13:18 -1000 Subject: Start with a basic /etc/hosts file (#491634) NetworkManager likes having an /etc/hosts file to start with on the system. It doesn't want to "own" the file, only touch and poke at it. --- scripts/mk-images | 1 + scripts/upd-instroot | 1 + 2 files changed, 2 insertions(+) diff --git a/scripts/mk-images b/scripts/mk-images index db2fa9df0..e669a0b6c 100755 --- a/scripts/mk-images +++ b/scripts/mk-images @@ -596,6 +596,7 @@ makeinitrd() { install -m 644 $IMGPATH/etc/passwd $MBD_DIR/etc/passwd install -m 644 $IMGPATH/etc/group $MBD_DIR/etc/group install -m 644 $IMGPATH/etc/nsswitch.conf $MBD_DIR/etc/nsswitch.conf + install -m 644 $IMGPATH/etc/hosts $MBD_DIR/etc/hosts instbin $IMGPATH /usr/bin/mount $MBD_DIR /sbin/mount for mountcmd in $IMGPATH/usr/sbin/mount.* ; do diff --git a/scripts/upd-instroot b/scripts/upd-instroot index 8e86eae7f..8fab35324 100755 --- a/scripts/upd-instroot +++ b/scripts/upd-instroot @@ -321,6 +321,7 @@ etc/fonts etc/group etc/gtk-2.0/gtkrc* etc/hal +etc/hosts etc/im_palette.pal etc/imrc etc/iscsid.conf -- cgit From 06aad08c33244c8624e13a046bf39b55ccdbfe91 Mon Sep 17 00:00:00 2001 From: David Cantrell Date: Tue, 24 Mar 2009 15:41:58 -1000 Subject: New version. --- anaconda.spec | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index 75bbd2cf6..6eee9ce76 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.36 +Version: 11.5.0.37 Release: 1 License: GPLv2+ Group: Applications/System @@ -210,6 +210,39 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Tue Mar 24 2009 David Cantrell - 11.5.0.37-1 +- Start with a basic /etc/hosts file (#491634) (dcantrell) +- Do not flag every existing partition for resize (#491803) (dcantrell) +- Remove unused noformatCB() function. (dcantrell) +- Remove unnecessary istruefalse() function. (dcantrell) +- Build new _isys.so for updates.img if needed. (dcantrell) +- Get the UUID of each md array we create. (#491796) (dlehman) +- Call udev_settle after committing changes to a disk (#491529) (hdegoede) +- Be a little bit smarter about allocating space to grow parts. (#491761) + (dlehman) +- Check that partition is on the disk before trying to remove it. (#491997) + (dlehman) +- Work around a bug in mdadm incremental assembly. (dlehman) +- Use the same units (MB) for extent size that we do for everything else. + (dlehman) +- Put line breaks in between crypttab entries. (#491938) (dlehman) +- Register the NoDevFS class. (clumens) +- fslabels -> labels. (clumens) +- NFSDevice does not take exists= as a parameter. (clumens) +- Override _setDevice and _getDevice in NFS. (clumens) +- Move resolveDevice into the DeviceTree class. (clumens) +- Move most of the parseFSTab logic into its own function. (clumens) +- We don't even use partedUtils in this module. (clumens) +- PReP formats can never be active. (#491865) (dlehman) +- Move protectedPartition setup into storageInitialize (#491781). (clumens) +- Use the mount and unmount methods on OpticalDevice.format now. (clumens) +- Add a format for ISO9660 filesystems. (clumens) +- getDeviceByName does not expect the CD device to start with "/dev/" + (#491768). (clumens) +- Write the same arch to .discinfo as iutil.getArch() gives us (#490977). + (clumens) +- Don't remove partitions twice. (jgranado) + * Mon Mar 23 2009 David Cantrell - 11.5.0.36-1 - Add EFI, Apple Bootstrap, and PPC PReP Boot formats. (dlehman) - Remove all implicit calls to self.format.destroy from Device classes. -- cgit From bb4fb4f4288bab359d35464edb75871efc8d350c Mon Sep 17 00:00:00 2001 From: David Lehman Date: Tue, 24 Mar 2009 21:50:40 -0500 Subject: Fix typo. (#492042) --- storage/devicetree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index 93ac747c6..458e5a8da 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1270,7 +1270,7 @@ class DeviceTree(object): # clearpart --initlabel was specified initlabel = self.reinitializeDisks for protected in self.protectedPartitions: - disk_name = re.sub(r'p\d+$', protected) + disk_name = re.sub(r'p\d+$', '', protected) if disk_name != protected and \ disk_name == rs.name: initlabel = False -- cgit From f7099850b26140c568f72fed9982f3985b84164e Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Wed, 25 Mar 2009 14:07:43 +0100 Subject: Device is sometimes None. If its none there is no need to parse it. --- storage/formats/fs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/formats/fs.py b/storage/formats/fs.py index 935065125..8864b3aab 100644 --- a/storage/formats/fs.py +++ b/storage/formats/fs.py @@ -1012,7 +1012,7 @@ class NFS(FS): _type = "nfs" def _deviceCheck(self, devspec): - if not ":" in devspec: + if devspec is not None and ":" not in devspec: raise ValueError("device must be of the form :") @property -- cgit From f23357309e0d2e6a0f055b0ebdc6c9dd8036455b Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Wed, 25 Mar 2009 14:37:29 +0100 Subject: "vg" is not valide inside this if. Inside this if, "device" is the vg device that we need. --- storage/devicetree.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/storage/devicetree.py b/storage/devicetree.py index 458e5a8da..b9552bd98 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -1410,10 +1410,10 @@ class DeviceTree(object): elif not device.complete: # The user chose not to reinitialize. # hopefully this will ignore the vg components too. - self._removeDevice(vg) - lvm.lvm_cc_addFilterRejectRegexp(vg.name) - lvm.blacklistVG(vg.name) - for parent in vg.parents: + self._removeDevice(device) + lvm.lvm_cc_addFilterRejectRegexp(device.name) + lvm.blacklistVG(device.name) + for parent in device.parents: self._removeDevice(parent, moddisk=False) lvm.lvm_cc_addFilterRejectRegexp(parent.name) -- cgit From c0353833be369304d911508c28c5a60ec59d89a8 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 25 Mar 2009 10:45:11 -0400 Subject: Don't underflow on the busy cursor stack. --- storage/__init__.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index fd9f28996..557f75962 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -264,17 +264,14 @@ class Storage(object): luksDict=self.__luksDevs) self.devicetree.populate() self.fsset = FSSet(self.devicetree) - w.pop() except FSError as e: - if w: - w.pop() - self.anaconda.intf.messageWindow(_("Error"), _("Filesystem error detected, cannot continue."), custom_icon="error") sys.exit(0) finally: - w.pop() + if w: + w.pop() @property def devices(self): -- cgit From 67eb3b98f2cb351fe7d88d6c250faba3fe222f30 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 25 Mar 2009 14:34:55 -0400 Subject: Code fixes of errors shown by pylint (mgracik). This only fixes the serious errors that may affect F11 Beta. There's still a lot of patch left that fixes some more nitpicky stuff which can come in later. --- storage/__init__.py | 9 +++++---- storage/devicelibs/lvm.py | 1 + storage/devicelibs/mdraid.py | 3 +++ storage/devicelibs/swap.py | 2 +- storage/formats/mdraid.py | 2 +- storage/formats/swap.py | 4 +--- storage/iscsi.py | 2 +- 7 files changed, 13 insertions(+), 10 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 557f75962..3fefbb5e9 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -75,7 +75,7 @@ def storageInitialize(anaconda): device = storage.devicetree.resolveDevice(devspec) if device is None: - if self.getUpgrade(): + if anaconda.id.getUpgrade(): return else: anaconda.intf.messageWindow(_("Unknown Device"), @@ -455,7 +455,7 @@ class Storage(object): part.fileSystemType in ("ext3", "ext2", "fat16", "fat32"): dests.append(part.path, device.name) - if not parts: + if not disk.partitions: dests.append(device.path, device.name) return dests @@ -647,7 +647,7 @@ class Storage(object): return False return True elif device.format.type == "swap": - return True + return True # be safe for anything else and default to off return False @@ -864,7 +864,7 @@ class Storage(object): def writeKS(self, f): log.warning("Storage.writeKS not completely implemented") self.iscsi.writeKS(f) - self.zfcp.writeFS(f) + self.zfcp.writeKS(f) def getReleaseString(mountpoint): @@ -1144,6 +1144,7 @@ class FSSet(object): self.devicetree = devicetree self.cryptTab = None self.blkidTab = None + self.origFStab = None self.active = False self._dev = None self._devpts = None diff --git a/storage/devicelibs/lvm.py b/storage/devicelibs/lvm.py index 52d9315a5..0dedc8c24 100644 --- a/storage/devicelibs/lvm.py +++ b/storage/devicelibs/lvm.py @@ -27,6 +27,7 @@ import re import iutil from ..errors import * +from constants import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) diff --git a/storage/devicelibs/mdraid.py b/storage/devicelibs/mdraid.py index d02b6eae1..dec5f2dc3 100644 --- a/storage/devicelibs/mdraid.py +++ b/storage/devicelibs/mdraid.py @@ -28,6 +28,9 @@ from ..errors import * import gettext _ = lambda x: gettext.ldgettext("anaconda", x) +import logging +log = logging.getLogger("storage") + # raidlevels constants RAID10 = 10 RAID6 = 6 diff --git a/storage/devicelibs/swap.py b/storage/devicelibs/swap.py index 38000eb6f..090c92eed 100644 --- a/storage/devicelibs/swap.py +++ b/storage/devicelibs/swap.py @@ -23,7 +23,7 @@ import resource import iutil -import resource +import os from ..errors import * diff --git a/storage/formats/mdraid.py b/storage/formats/mdraid.py index 0871a2a5c..ec1a61782 100644 --- a/storage/formats/mdraid.py +++ b/storage/formats/mdraid.py @@ -69,7 +69,7 @@ class MDRaidMember(DeviceFormat): log_method_call(self, device=self.device, type=self.type, status=self.status) if not self.exists: - raise MDRaidMemberError("format does not exist") + raise MDMemberError("format does not exist") info = mdraid.mdexamine(self.device) if self.uuid is None: diff --git a/storage/formats/swap.py b/storage/formats/swap.py index e5dd8ef3d..bc5dbd90f 100644 --- a/storage/formats/swap.py +++ b/storage/formats/swap.py @@ -20,9 +20,7 @@ # Red Hat Author(s): Dave Lehman # -import os - -from iutil import log_method_call +from iutil import log_method_call, numeric_type from parted import PARTITION_SWAP from ..errors import * from ..devicelibs import swap diff --git a/storage/iscsi.py b/storage/iscsi.py index b9f975e26..29d89830e 100644 --- a/storage/iscsi.py +++ b/storage/iscsi.py @@ -287,7 +287,7 @@ class iscsi(object): if node.name == disk.iscsi_name and \ node.address == disk.iscsi_address and \ node.port == disk.iscsi_port: - node.setParameter("node.startup", "automatic"); + node.setParameter("node.startup", "automatic") break if not os.path.isdir(instPath + "/etc/iscsi"): -- cgit From 4fc6b22471f4d0534ba46811509ec1cd1a60e7fb Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 25 Mar 2009 14:48:24 -0400 Subject: Rework CryptTab.parse (dlehman). --- storage/__init__.py | 51 +++++++++++++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/storage/__init__.py b/storage/__init__.py index 3fefbb5e9..16a69222d 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -1051,33 +1051,32 @@ class CryptTab(object): path = "%s/etc/crypttab" % chroot log.debug("parsing %s" % path) with open(path) as f: + if not self.blkidTab: + try: + self.blkidTab = BlkidTab(chroot=chroot) + self.blkidTab.parse() + except Exception: + self.blkidTab = None + for line in f.readlines(): - if not self.blkidTab: - try: - self.blkidTab = BlkidTab(chroot=chroot) - self.blkidTab.parse() - except Exception: - self.blkidTab = None - - for line in lines: - (line, pound, comment) = line.partition("#") - fields = line.split() - if not 2 <= len(fields) <= 4: - continue - elif len(fields) == 2: - fields.extend(['none', '']) - elif len(fields) == 3: - fields.append('') - - (name, devspec, keyfile, options) = fields - - # resolve devspec to a device in the tree - device = self.devicetree.resolveDevice(devspec, - blkidTab=self.blkidTab) - if device: - self.mappings[name] = {"device": device, - "keyfile": keyfile, - "options": options} + (line, pound, comment) = line.partition("#") + fields = line.split() + if not 2 <= len(fields) <= 4: + continue + elif len(fields) == 2: + fields.extend(['none', '']) + elif len(fields) == 3: + fields.append('') + + (name, devspec, keyfile, options) = fields + + # resolve devspec to a device in the tree + device = self.devicetree.resolveDevice(devspec, + blkidTab=self.blkidTab) + if device: + self.mappings[name] = {"device": device, + "keyfile": keyfile, + "options": options} def populate(self): """ Populate the instance based on the device tree's contents. """ -- cgit From 4c6bdd9b2654500c7502030231764047213f5d07 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Wed, 25 Mar 2009 20:44:49 +0100 Subject: Fix pylint errors in iw/*.py --- iw/lvm_dialog_gui.py | 2 +- iw/raid_dialog_gui.py | 2 +- iw/upgrade_migratefs_gui.py | 5 +++++ 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/iw/lvm_dialog_gui.py b/iw/lvm_dialog_gui.py index 5bb073213..2cee546ba 100644 --- a/iw/lvm_dialog_gui.py +++ b/iw/lvm_dialog_gui.py @@ -447,7 +447,7 @@ class VolumeGroupEditor: if lv['name']: lvnameEntry.set_text(lv['name']) else: - lvnameEntry.set_text(storage.createSuggestedLVName(self.getTempVG())) + lvnameEntry.set_text(self.storage.createSuggestedLVName(self.getTempVG())) else: lbl = createAlignedLabel(_("Logical Volume Name:")) lvnameEntry = gtk.Label(lv['name']) diff --git a/iw/raid_dialog_gui.py b/iw/raid_dialog_gui.py index c8062047f..177c4c366 100644 --- a/iw/raid_dialog_gui.py +++ b/iw/raid_dialog_gui.py @@ -621,7 +621,7 @@ class RaidCloneDialog: self.intf.messageWindow(_("Target Drive Error"), _("The source drive %s cannot be " "selected as a target drive as well.") - % (self.sourceDev.path,), + % (sourceDev.path,), custom_icon="error") return 1 diff --git a/iw/upgrade_migratefs_gui.py b/iw/upgrade_migratefs_gui.py index 42397cfaf..428ead255 100644 --- a/iw/upgrade_migratefs_gui.py +++ b/iw/upgrade_migratefs_gui.py @@ -21,6 +21,8 @@ from iw_gui import * from constants import * +from storage.formats import getFormat +from storage.deviceaction import ActionMigrateFormat import string import isys import iutil @@ -29,6 +31,9 @@ import gtk import gettext _ = lambda x: gettext.ldgettext("anaconda", x) +import logging +log = logging.getLogger("anaconda") + class UpgradeMigrateFSWindow (InstallWindow): windowTitle = N_("Migrate File Systems") -- cgit From e0a19200de4b7a0cb72333939131d816c656a8b4 Mon Sep 17 00:00:00 2001 From: Chris Lumens Date: Wed, 25 Mar 2009 15:50:28 -0400 Subject: New version. --- anaconda.spec | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/anaconda.spec b/anaconda.spec index 6eee9ce76..49f45ad44 100644 --- a/anaconda.spec +++ b/anaconda.spec @@ -2,7 +2,7 @@ Summary: Graphical system installer Name: anaconda -Version: 11.5.0.37 +Version: 11.5.0.38 Release: 1 License: GPLv2+ Group: Applications/System @@ -210,6 +210,15 @@ update-desktop-database &> /dev/null || : %endif %changelog +* Wed Mar 25 2009 Chris Lumens - 11.5.0.38-1 +- Fix pylint errors in iw/*.py (hdegoede) +- Rework CryptTab.parse (dlehman). +- Code fixes of errors shown by pylint (mgracik). +- Don't underflow on the busy cursor stack. (clumens) +- "vg" is not valide inside this if. (jgranado) +- Device is sometimes None. (jgranado) +- Fix typo. (#492042) (dlehman) + * Tue Mar 24 2009 David Cantrell - 11.5.0.37-1 - Start with a basic /etc/hosts file (#491634) (dcantrell) - Do not flag every existing partition for resize (#491803) (dcantrell) -- cgit From 420a2afd509f532eb34adbb1c842f0445d184fee Mon Sep 17 00:00:00 2001 From: David Lehman Date: Wed, 25 Mar 2009 11:33:56 -0500 Subject: Don't do format-specific processing on devices targeted by clearpart. (#492123) --- cmdline.py | 14 ++++++++++++++ storage/__init__.py | 2 ++ storage/devicetree.py | 17 ++++++++++++++++- 3 files changed, 32 insertions(+), 1 deletion(-) diff --git a/cmdline.py b/cmdline.py index f87550c46..9b161ab80 100644 --- a/cmdline.py +++ b/cmdline.py @@ -104,6 +104,20 @@ class InstallInterface: while 1: time.sleep(5) + def passphraseEntryWindow(self, device): + print(_("Can't have a question in command line mode!")) + print("(passphraseEntryWindow: '%s')" % device.path) + # don't exit + while 1: + time.sleep(5) + + def getLUKSPassphrase(self, passphrase = "", isglobal = False): + print(_("Can't have a question in command line mode!")) + print("(getLUKSPassphrase)") + # don't exit + while 1: + time.sleep(5) + def exceptionWindow(self, shortText, longTextFile): print(shortText) diff --git a/storage/__init__.py b/storage/__init__.py index 16a69222d..09eef560d 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -199,6 +199,7 @@ class Storage(object): ignored=self.ignoredDisks, exclusive=self.exclusiveDisks, clear=self.clearPartDisks, + clearpart=self.clearPartType, reinitializeDisks=self.reinitializeDisks, protected=self.protectedPartitions, zeroMbr=self.zeroMbr, @@ -257,6 +258,7 @@ class Storage(object): ignored=self.ignoredDisks, exclusive=self.exclusiveDisks, clear=self.clearPartDisks, + clearpart=self.clearPartType, reinitializeDisks=self.reinitializeDisks, protected=self.protectedPartitions, zeroMbr=self.zeroMbr, diff --git a/storage/devicetree.py b/storage/devicetree.py index b9552bd98..0533abd88 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -24,6 +24,8 @@ import os import block import re +from pykickstart.constants import * + from errors import * from devices import * from deviceaction import * @@ -188,13 +190,14 @@ class DeviceTree(object): def __init__(self, intf=None, ignored=[], exclusive=[], clear=[], zeroMbr=None, reinitializeDisks=None, protected=[], - passphrase=None, luksDict=None): + clearpart=None, passphrase=None, luksDict=None): # internal data members self._devices = [] self._actions = [] self.intf = intf self.exclusiveDisks = exclusive + self.clearPartType = clearpart self.clearPartDisks = clear self.zeroMbr = zeroMbr self.reinitializeDisks = reinitializeDisks @@ -1156,6 +1159,18 @@ class DeviceTree(object): format = formats.getFormat(*args, **kwargs) device.format = format + if self.clearPartType in (CLEARPART_TYPE_LINUX, CLEARPART_TYPE_ALL): + if not self.clearPartDisks: + # we're clearing all disks + return + + for disk in self.clearPartDisks: + # if this device is going to be cleared we don't need to + # continue messing with it + dev = self.getDeviceByName(disk) + if dev and dev == device or device.dependsOn(dev): + return + # # now lookup or create any compound devices we have discovered # -- cgit From a6cca07d229f15c05e582590f974ebb43bd66290 Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Fri, 27 Mar 2009 16:15:01 +0100 Subject: Revert "Don't do format-specific processing on devices targeted by clearpart. (#492123)" This reverts commit 420a2afd509f532eb34adbb1c842f0445d184fee. I messed up here with a dirty branch. --- cmdline.py | 14 -------------- storage/__init__.py | 2 -- storage/devicetree.py | 17 +---------------- 3 files changed, 1 insertion(+), 32 deletions(-) diff --git a/cmdline.py b/cmdline.py index 9b161ab80..f87550c46 100644 --- a/cmdline.py +++ b/cmdline.py @@ -104,20 +104,6 @@ class InstallInterface: while 1: time.sleep(5) - def passphraseEntryWindow(self, device): - print(_("Can't have a question in command line mode!")) - print("(passphraseEntryWindow: '%s')" % device.path) - # don't exit - while 1: - time.sleep(5) - - def getLUKSPassphrase(self, passphrase = "", isglobal = False): - print(_("Can't have a question in command line mode!")) - print("(getLUKSPassphrase)") - # don't exit - while 1: - time.sleep(5) - def exceptionWindow(self, shortText, longTextFile): print(shortText) diff --git a/storage/__init__.py b/storage/__init__.py index 09eef560d..16a69222d 100644 --- a/storage/__init__.py +++ b/storage/__init__.py @@ -199,7 +199,6 @@ class Storage(object): ignored=self.ignoredDisks, exclusive=self.exclusiveDisks, clear=self.clearPartDisks, - clearpart=self.clearPartType, reinitializeDisks=self.reinitializeDisks, protected=self.protectedPartitions, zeroMbr=self.zeroMbr, @@ -258,7 +257,6 @@ class Storage(object): ignored=self.ignoredDisks, exclusive=self.exclusiveDisks, clear=self.clearPartDisks, - clearpart=self.clearPartType, reinitializeDisks=self.reinitializeDisks, protected=self.protectedPartitions, zeroMbr=self.zeroMbr, diff --git a/storage/devicetree.py b/storage/devicetree.py index 0533abd88..b9552bd98 100644 --- a/storage/devicetree.py +++ b/storage/devicetree.py @@ -24,8 +24,6 @@ import os import block import re -from pykickstart.constants import * - from errors import * from devices import * from deviceaction import * @@ -190,14 +188,13 @@ class DeviceTree(object): def __init__(self, intf=None, ignored=[], exclusive=[], clear=[], zeroMbr=None, reinitializeDisks=None, protected=[], - clearpart=None, passphrase=None, luksDict=None): + passphrase=None, luksDict=None): # internal data members self._devices = [] self._actions = [] self.intf = intf self.exclusiveDisks = exclusive - self.clearPartType = clearpart self.clearPartDisks = clear self.zeroMbr = zeroMbr self.reinitializeDisks = reinitializeDisks @@ -1159,18 +1156,6 @@ class DeviceTree(object): format = formats.getFormat(*args, **kwargs) device.format = format - if self.clearPartType in (CLEARPART_TYPE_LINUX, CLEARPART_TYPE_ALL): - if not self.clearPartDisks: - # we're clearing all disks - return - - for disk in self.clearPartDisks: - # if this device is going to be cleared we don't need to - # continue messing with it - dev = self.getDeviceByName(disk) - if dev and dev == device or device.dependsOn(dev): - return - # # now lookup or create any compound devices we have discovered # -- cgit From a88b876ee50687de6396229fef0a77c232cceece Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Fri, 27 Mar 2009 11:38:57 +0100 Subject: Use self.name to report that we could not eject cd. --- storage/devices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devices.py b/storage/devices.py index 2f1319e34..9f8270ad6 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -2684,7 +2684,7 @@ class OpticalDevice(StorageDevice): try: _isys.ejectcdrom(fd) except SystemError as e: - log.warning("error ejecting cdrom %s: %s" % (device, e)) + log.warning("error ejecting cdrom %s: %s" % (self.name, e)) os.close(fd) -- cgit From dfe423b3cab7b850efe949b7ab8b196bf1c6b876 Mon Sep 17 00:00:00 2001 From: Joel Granados Moreno Date: Fri, 27 Mar 2009 13:25:14 +0100 Subject: Handle not finding the upgrade root gracefully. --- upgrade.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/upgrade.py b/upgrade.py index be701fb24..6f6ddd05c 100644 --- a/upgrade.py +++ b/upgrade.py @@ -241,6 +241,21 @@ def upgradeMountFilesystems(anaconda): "systems listed in /etc/fstab. Please fix this problem " "and try to upgrade again.\n%s" % e)) sys.exit(0) + except IndexError as e: + # The upgrade root is search earlier but we give the message here. + log.debug("No upgrade root was fond.") + rc = anaconda.intf.messageWindow(_("Upgrade root not found"), + _("The root for the previously installed system was not " + "found. You can exit installer or backtrack to choose " + "installation instead of upgrade."), + type="custom", + custom_buttons = [ _("_Back"), + _("_Exit installer") ], + custom_icon="question") + if rc == 0: + return DISPATCH_BACK + elif rc == 1: + sys.exit(0) checkLinks = ( '/etc', '/var', '/var/lib', '/var/lib/rpm', '/boot', '/tmp', '/var/tmp', '/root', -- cgit From 610a0a547c3061167f57ff87613a8e1b768f396e Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Fri, 27 Mar 2009 13:45:56 +0100 Subject: Grow LVs for kickstart requests too --- storage/partitioning.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/storage/partitioning.py b/storage/partitioning.py index 9f42a7299..a71ba91de 100644 --- a/storage/partitioning.py +++ b/storage/partitioning.py @@ -147,8 +147,6 @@ def _scheduleLVs(anaconda, devs): # schedule the device for creation anaconda.id.storage.createDevice(dev) - # grow the new VG and its LVs - growLVM(anaconda.id.storage) def doAutoPartition(anaconda): log.debug("doAutoPartition(%s)" % anaconda) @@ -216,6 +214,9 @@ def doAutoPartition(anaconda): if anaconda.id.storage.doAutoPart: _scheduleLVs(anaconda, devs) + # grow LVs + growLVM(anaconda.id.storage) + # sanity check the collection of devices log.warning("not sanity checking storage config because I don't know how yet") # now do a full check of the requests -- cgit From b74a30461c62520e08b5972607257149901ef473 Mon Sep 17 00:00:00 2001 From: Radek Vykydal Date: Fri, 27 Mar 2009 13:47:11 +0100 Subject: Keep VG size property non-negative --- storage/devices.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage/devices.py b/storage/devices.py index 9f8270ad6..1e67e801e 100644 --- a/storage/devices.py +++ b/storage/devices.py @@ -1749,7 +1749,7 @@ class LVMVolumeGroupDevice(DMDevice): # sum up the sizes of the PVs and align to pesize size = 0 for pv in self.pvs: - size += self.align(pv.size - pv.format.peStart) + size += max(0, self.align(pv.size - pv.format.peStart)) return size -- cgit