diff options
-rw-r--r-- | dispatch.py | 212 | ||||
-rw-r--r-- | fsset.py | 859 | ||||
-rw-r--r-- | partitioning.py | 561 |
3 files changed, 1632 insertions, 0 deletions
diff --git a/dispatch.py b/dispatch.py new file mode 100644 index 000000000..031890ce4 --- /dev/null +++ b/dispatch.py @@ -0,0 +1,212 @@ +import string +from types import * +from packages import readPackages, checkDependencies, doInstall, handleX11Packages +from packages import writeConfiguration, writeXConfiguration, writeKSConfiguration, turnOnFilesystems, queryUpgradeContinue +from partitioning import AutoPartition +from iutil import makeBootdisk +from bootloader import partitioningComplete, writeBootloader +from flags import flags + +DISPATCH_BACK = -1 +DISPATCH_FORWARD = 1 +DISPATCH_NOOP = None + +# These are all of the install steps, in order. Note that upgrade and +# install steps are the same thing! Upgrades skip install steps, while +# installs skip upgrade steps. + +# +# items are one of +# +# ( name, tuple) +# ( name, Function, tuple) +# +# in the second case, the function is called directly from the dispatcher + +installSteps = [ +# XXX for msw partition development +# ( "partition", ("id.fsset", "id.diskset", "id.partrequests") ), + ( "language", ("intf", "id.instLanguage") ), + ( "keyboard", ("id.instLanguage", "id.keyboard") ), + ( "mouse", ("id.mouse", ) ), +# +# uncomment next 4 steps to debug X config easier +# +# ( "videocard", ("dispatch", "id.xconfig", "id.videocard")), +# ( "monitor", ("id.xconfig", "id.monitor") ), +# ( "xcustom", ("id.xconfig", "id.monitor", "id.videocard", +# "id.desktop", "id.comps") ), +# ( "writexconfig", writeXConfiguration, ("id", "instPath")), + ( "welcome", () ), + ( "reconfigwelcome", () ), + ( "reconfigkeyboard", ("id.instLanguage", "id.keyboard" ) ), + ( "installtype", ("dispatch", "id", "method", + "intf") ), + ( "partition", ("id.fsset", "id.diskset", "id.partrequests", + "intf")), + #( "partitionmethod", ( "dispatch", )), + #( "fdisk", ( "id.diskset", )), + #( "custom-upgrade", () ), + #( "addswap", () ), + #( "fdisk", () ), + #( "partition", () ), + ( "partitiondone", partitioningComplete, + ("dispatch", "id.bootloader", + "id.fsset", "id.diskset" ) ), + ( "bootloader", ("dispatch", "id.bootloader", + "id.fsset", "id.diskset") ), + ( "network", ("id.network",) ), + ( "firewall", ("id.network", "id.firewall") ), + ( "languagesupport", ("id.langSupport", ) ), + ( "timezone", ("id.instLanguage", "id.timezone", ) ), + ( "accounts", ("id.rootPassword", "id.accounts", ) ), + ( "authentication", ("id.auth", ) ), + ( "readcomps", readPackages, ("intf", "method", "id" )), + ( "upgradecontinue", queryUpgradeContinue, ("intf", "dir", + "dispatch")), + ( "package-selection", ("id.comps", "dispatch") ), + ( "indivpackage", ("id.comps", "id.hdList", ) ), + ( "handleX11pkgs", handleX11Packages, + ("dir", "intf", "dispatch", "id", "instPath" )), + ( "videocard", ("dispatch", "id.xconfig", "id.videocard")), + ( "checkdeps", checkDependencies, + ("dir", "intf", "dispatch", "id", "instPath" )), + ( "dependencies", ("id.comps", "id.dependencies",) ), + ( "confirminstall", () ), + ( "confirmupgrade", () ), + ( "install", ("dir", "intf", "id", ) ), + ( "enablefilesystems", turnOnFilesystems, + ( "dir", "id.fsset", "id.diskset", + "id.upgrade", "instPath") ), + ( "installpackages", doInstall, + ( "method", "id", "intf", "instPath" )), + ( "writeconfig", writeConfiguration, + ("id", "instPath" )), + ( "instbootloader", writeBootloader, + ("intf", "instPath", "id.fsset", + "id.bootloader", "id.langSupport", "id.comps") ), + ( "monitor", ("id.xconfig", "id.monitor") ), + ( "xcustom", ("id.xconfig", "id.monitor", "id.videocard", + "id.desktop", "id.comps") ), + ( "writexconfig", writeXConfiguration, ("id", "instPath")), + ( "writeksconfig", writeKSConfiguration, ("id", "instPath")), + ( "bootdisk", ("dir", "dispatch") ), + ( "makebootdisk", makeBootdisk, ("intf",) ), + ( "complete", () ), + ( "reconfigcomplete", () ) + ] + +class Dispatcher: + + def gotoPrev(self): + self.dir = -1 + self.moveStep() + + def gotoNext(self): + self.dir = 1 + self.moveStep() + + def setStepList(self, *steps): + self.skipSteps = {} + stepExists = {} + for step in installSteps: + name = step[0] + if not name in steps: + self.skipSteps[name] = 1 + + stepExists[name] = 1 + + for name in steps: + if not stepExists.has_key(name): + raise KeyError, ("step %s does not exist" % name) + + def stepInSkipList(self, step): + return self.skipSteps.has_key(step) + + def skipStep(self, stepToSkip, skip = 1): + for step in installSteps: + name = step[0] + if name == stepToSkip: + if skip: + self.skipSteps[name] = 1 + elif self.skipSteps.has_key(name): + del self.skipSteps[name] + return + + raise KeyError, ("unknown step %s" % stepToSkip) + + def moveStep(self): + if self.step == None: + self.step = self.firstStep + else: + self.step = self.step + self.dir + + if self.step == len(installSteps): + return None + + while ((self.step >= self.firstStep + and self.step < len(installSteps)) + and (self.skipSteps.has_key(installSteps[self.step][0]) + or (type(installSteps[self.step][1]) == FunctionType))): + info = installSteps[self.step] + if ((type(info[1]) == FunctionType) + and (not self.skipSteps.has_key(info[0]))): + (func, args) = info[1:] + rc = apply(func, self.bindArgs(args)) + if rc == DISPATCH_BACK: + self.dir = -1 + elif rc == DISPATCH_FORWARD: + self.dir = 1 + # if anything else, leave self.dir alone + + self.step = self.step + self.dir + if self.step == len(installSteps): + return None + + if (self.step < 0): + # pick the first step not in the skip list + self.step = 0 + while self.skipSteps.has_key(installSteps[self.step][0]): + self.step = self.step + 1 + elif self.step >= len(installSteps): + self.step = len(installSteps) - 1 + while self.skipSteps.has_key(installSteps[self.step][0]): + self.step = self.step - 1 + + def bindArgs(self, args): + newArgs = () + for arg in args: + obj = self + for item in string.split(arg, '.'): + if not obj.__dict__.has_key(item): + print "cannot find %s in %s" % (item, obj) + obj = obj.__dict__[item] + newArgs = newArgs + (obj,) + + return newArgs + + def currentStep(self): + if self.step == None: + self.gotoNext() + elif self.step == len(installSteps): + return (None, None) + + stepInfo = installSteps[self.step] + step = stepInfo[0] + args = self.bindArgs(stepInfo[1]) + + return (step, args) + + def __init__(self, intf, id, method, instPath): + self.dir = DISPATCH_FORWARD + self.step = None + self.skipSteps = {} + dispatch = self + + self.id = id + self.flags = flags + self.intf = intf + self.method = method + self.dispatch = self + self.instPath = instPath + self.firstStep = 0 diff --git a/fsset.py b/fsset.py new file mode 100644 index 000000000..927c6cd6d --- /dev/null +++ b/fsset.py @@ -0,0 +1,859 @@ +# +# fstab.py: filesystem management +# +# Matt Wilson <msw@redhat.com> +# +# Copyright 2001 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 string +import isys +import iutil +import os +import parted +from log import log +from translate import _, N_ +import partitioning + +defaultMountPoints = ('/', '/boot', '/home', '/tmp', '/usr', '/var') + +fileSystemTypes = {} + +def fileSystemTypeGetDefault(): + return fileSystemTypeGet('ext2') + +def fileSystemTypeGet(key): + return fileSystemTypes[key] + +def fileSystemTypeRegister(klass): + fileSystemTypes[klass.getName()] = klass + +def fileSystemTypeGetTypes(): + return fileSystemTypes.copy() + +def mountCompare(a, b): + one = a.mountpoint + two = b.mountpoint + if one < two: + return -1 + elif two > one: + return 1 + return 0 + +def devify(device): + if device != "none": + return "/dev/" + device + return device + +class LabelFactory: + def __init__(self): + self.labels = {} + + def createLabel(self, mountpoint): + if len(mountpoint) > 16: + mountpoint = mountpoint[0:16] + count = 0 + while self.labels.has_key(mountpoint): + count = count + 1 + s = "%s" % count + if (len(mountpoint) + len(s)) <= 16: + mountpoint = mountpoint + s + else: + strip = len(mountpoint) + len(s) - 16 + mountpoint = mountpoint[0:len(mountpoint) - strip] + s + self.labels[mountpoint] = 1 + + return mountpoint + + def reserveLabels(self, labels): + for device, label in labels.items(): + self.labels[label] = 1 + +labelFactory = LabelFactory() + +class FileSystemType: + kernelFilesystems = {} + def __init__(self): + self.deviceArguments = {} + self.formattable = 0 + self.checked = 0 + self.name = "" + self.linuxnativefs = 0 + self.partedFileSystemType = None + self.partedPartitonFlags = [] + + def getName(self): + return self.name + + def registerDeviceArgumentFunction(self, klass, function): + self.deviceArguments[klass] = function + + def formatDevice(self, devicePath, device, progress, message, chroot='/'): + raise RuntimeError, "formatDevice method not defined" + + def isFormattable(self): + return self.formattable + + def isLinuxNativeFS(self): + return self.linuxnativefs + + 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() + + return FileSystemType.kernelFilesystems.has_key(self.getName()) + + 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 + +class ext2FileSystem(FileSystemType): + def __init__(self): + FileSystemType.__init__(self) + self.partedFileSystemType = parted.file_system_type_get("ext2") + self.formattable = 1 + self.checked = 1 + self.linuxnativefs = 1 + self.name = "ext2" + + def formatDevice(self, entry, progress, message, chroot='/'): + devicePath = entry.device.setupDevice(chroot) + devArgs = self.getDeviceArgs(entry.device) + label = labelFactory.createLabel(entry.mountpoint) + entry.setLabel(label) + args = [ "/usr/sbin/mke2fs", devicePath, '-L', label ] + args.extend(devArgs) + + rc = ext2FormatFilesystem(args, "/dev/tty5", + progress, + entry.mountpoint) + if rc: + message and message(_("Error"), + _("An error occured trying to format %s. " + "This problem is serious, and the install " + "cannot continue.\n\n" + "Press Enter to reboot your " + "system.") % (entry.device.getDevice(),)) + raise SystemError + +fileSystemTypeRegister(ext2FileSystem()) + +class raidMemberDummyFileSystem(FileSystemType): + def __init__(self): + FileSystemType.__init__(self) + self.partedFileSystemType = parted.file_system_type_get("ext2") + self.partedPartitionFlags = [ parted.PARTITION_RAID ] + self.formattable = 1 + self.checked = 0 + self.linuxnativefs = 0 + self.name = "software raid component" + + def formatDevice(self, entry, progress, message, chroot='/'): + # mkraid did all we need to format this partition... + pass + +fileSystemTypeRegister(raidMemberDummyFileSystem()) + +class swapFileSystem(FileSystemType): + def __init__(self): + FileSystemType.__init__(self) + self.partedFileSystemType = parted.file_system_type_get("linux-swap") + self.formattable = 1 + self.name = "swap" + + def formatDevice(self, entry, progress, message, chroot='/'): + file = entry.device.setupDevice(chroot) + rc = iutil.execWithRedirect ("/usr/sbin/mkswap", + [ "mkswap", '-v1', file ], + stdout = None, stderr = None, + searchPath = 1) + +fileSystemTypeRegister(swapFileSystem()) + +class FATFileSystem(FileSystemType): + def __init__(self): + FileSystemType.__init__(self) + self.partedFileSystemType = parted.file_system_type_get("FAT") + self.formattable = 0 + self.checked = 0 + self.name = "vfat" + +fileSystemTypeRegister(FATFileSystem()) + +class ForeignFileSystem(FileSystemType): + def __init__(self): + FileSystemType.__init__(self) + self.formattable = 0 + self.checked = 0 + self.name = "foreign" + + def formatDevice(self, entry, progress, message, chroot='/'): + return + +fileSystemTypeRegister(ForeignFileSystem()) + +class FileSystemSet: + def __init__(self): + self.entries = [] + self.messageWindow = None + self.progressWindow = None + + def registerMessageWindow(self, method): + self.messageWindow = method + + def registerProgressWindow(self, method): + self.progressWindow = method + + def reset (self): + self.entries = [] + + def add (self, entry): + self.entries.append(entry) + self.entries.sort (mountCompare) + + 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 + 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 = "" + for entry in self.entries: + if entry.mountpoint: + if entry.getLabel(): + device = "LABEL=%s" % (entry.getLabel(),) + else: + device = devify(entry.device.getDevice()) + fstab = fstab + entry.device.getComment() + fstab = fstab + format % (device, entry.mountpoint, + entry.fsystem.getName(), + entry.options, entry.fsck, + entry.order) + return fstab + + 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 write (self, prefix): + f = open (prefix + "/etc/fstab", "w") + f.write (self.fstab()) + f.close () + + raidtab = self.raidtab() + + if raidtab: + f = open (prefix + "/etc/fstab", "w") + f.write (raidtab) + f.close () + + # touch mtab + open (prefix + "/etc/mtab", "w+") + f.close () + + def rootOnLoop (self): + for entry in self.entries: + if (entry.mountpoint == '/' + and entry.device.getName() == "LoopbackDevice"): + return 1 + return 0 + + def bootloaderChoices(self, diskSet): + mntDict = {} + + for entry in self.entries: + mntDict[entry.mountpoint] = entry.device + + if mntDict.has_key("/boot"): + bootDev = mntDict['/boot'] + else: + bootDev = mntDict['/'] + + if bootDev.getName() == "LoopbackDevice": + return None + elif bootDev.getName() == "RAIDDevice": + return [ bootDev.device, "RAID Device" ] + + return [ (diskSet.driveList()[0], N_("Master Boot Record (MBR)") ), + (bootDev.device, N_("First sector of boot partition")) + ] + + def formatSwap (self, chroot): + for entry in self.entries: + if entry.mountpoint and entry.fsystem.getName() == "swap": + entry.fsystem.formatDevice(entry, self.progressWindow, + self.messageWindow, chroot) + + def turnOnSwap (self, chroot): + for entry in self.entries: + if entry.mountpoint and entry.fsystem.getName() == "swap": + if not entry.isMounted(): + file = entry.device.setupDevice(chroot) + isys.swapon (file) + entry.setMounted(1) + + def turnOffSwap(self, devices = 1, files = 0): + for entry in self.entries: + if entry.mountpoint and entry.fsystem.getName() == "swap": + if entry.isMounted(): + isys.swapoff(n) + entry.setMounted(0) + + def formattablePartitions(self): + list = [] + for entry in self.entries: + if entry.fsystem.isFormattable(): + list.append (entry) + return list + + def makeFilesystems (self, chroot='/'): + for entry in self.entries: + if (not entry.format or entry.isMounted() + or not entry.fsystem.isFormattable()): + continue + + entry.fsystem.formatDevice(entry, self.progressWindow, + self.messageWindow, chroot) + + def mountFilesystems(self, instPath = '/', raiseErrors = 0, readOnly = 0): + for entry in self.entries: + if not entry.fsystem.isMountable(): + continue + elif (entry.fsystem.isFormattable() + or (entry.fsystem.getName() == "vfat" + and entry.mountpoint == "/boot/efi")): + try: + iutil.mkdirChain(instPath + entry.mountpoint) + isys.mount(entry.device.getDevice(), + instPath + entry.mountpoint, + fstype = entry.fsystem.getName(), + readOnly = readOnly) + entry.setMounted(1) + except SystemError, (errno, msg): + if raiseErrors: + raise SystemError, (errno, msg) + self.messageWindow and self.messageWindow(_("Error"), + _("Error mounting device %s as %s: %s\n\n" + "This most likely means this partition has " + "not been formatted.\n\nPress OK to reboot your " + "system.") % (entry.device.getDevice(), + entry.mountpoint, msg)) + sys.exit(0) + + try: + os.mkdir (instPath + '/proc') + except: + pass + + isys.mount('/proc', instPath + '/proc', 'proc') + + def umountFilesystems(self, instPath, ignoreErrors = 0): + try: + isys.umount(instPath + '/proc/bus/usb', removeDir = 0) + log("Umount USB OK") + except: +# log("Umount USB Fail") + pass + + reverse = self.entries + reverse.reverse() + + for entry in reverse: + if entry.isMounted(): + isys.umount(instPath + entry.mountpoint, removeDir = 0) + entry.setMounted(0) + +class FileSystemSetEntry: + def __init__ (self, device, mountpoint, + fsystem=None, options="defaults", + order=-1, fsck=-1, format=0): + if not fsystem: + fsystem = fileSystemTypeGet("ext2") + self.device = device + self.mountpoint = mountpoint + self.fsystem = fsystem + self.options = options + self.mounted = 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 + + def setFormat (self, state): + self.format = state + + def getFormat (self): + return self.format + + def isMounted (self): + return self.mounted + + def setMounted (self, state): + self.isMounted = state + + def getLabel (self): + return self.label + + def setLabel (self, label): + self.label = label + +class Device: + def __init__(self): + self.device = "none" + self.fsoptions = {} + self.label = None + + def __repr__(self): + return self.device + + def getComment (self): + return "" + + def getDevice (self): + return self.device + + def setupDevice (self, chroot='/tmp'): + pass + + def cleanupDevice (self, chroot, devPrefix='/tmp'): + pass + + def solidify (self): + pass + + def getName(self): + return self.__class__.__name__ + +class RAIDDevice(Device): + 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): + Device.__init__(self) + self.level = level + self.members = members + self.spares = spares + self.numDisks = len(members) - spares + + if len(members) < spares: + raise RuntimeError, "you requiested 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: + for I in range(32): + if not I in RAIDDevice.usedMajors: + minor = I + break + raise RuntimeError, "Unable to allocate minor number for raid device" + minor = I + RAIDDevice.usedMajors.append(minor) + self.device = "md" + str(minor) + self.minor = minor + + def __del__ (self): + RAIDDevice.usedMajors.remove(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) ] + + 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 64k\n" + entry = entry + "persistent-superblock 1\n" + entry = entry + "#nr-spare-disks %d\n" % (self.spares,) + i = 0 + for device in self.members: + entry = entry + " device %s/%s\n" % (devPrefix, + device.getDevice()) + entry = entry + " raid-disk %d\n" % (i,) + i = i + 1 + return entry + + def setupDevice (self, chroot, devPrefix='/tmp'): + raidtab = '/tmp/raidtab.%s' % (self.device,) + f = open(raidtab, 'w') + f.write(self.raidTab('/tmp')) + for device in self.members: + device.setupDevice() + node = "%s/%s" % (devPrefix, self.device) + isys.makeDevInode(self.device, node, devPrefix=devPrefix) + iutil.execWithRedirect ("/usr/sbin/mkraid", + [ 'mkraid', '--really-force', '--configfile', + raidtab, node ], + stderr = "/dev/tty5", stdout = "/dev/tty5") + return node + + def solidify(self): + for device in self.members: + device.solidify() + +ext2 = fileSystemTypeGet("ext2") +ext2.registerDeviceArgumentFunction(RAIDDevice, RAIDDevice.ext2Args) + +class LVMDevice(Device): + def __init__(self): + Device.__init__(self) + +class PartitionDevice(Device): + def __init__(self, partition): + Device.__init__(self) + self.device = partition + + def setupDevice(self, chroot, devPrefix='/tmp'): + path = '%s/%s' % (devPrefix, self.getDevice(),) + isys.makeDevInode(self.getDevice(), path) + return path + +class PartedPartitionDevice(Device): + def __init__(self, partition): + Device.__init__(self) + self.partition = partition + self.device = None + + def getDevice(self): + if not self.partition: + return self.device + + if (self.partition.geom.disk.dev.type == parted.DEVICE_DAC960 + or self.partition.geom.disk.dev.type == parted.DEVICE_CPQARRAY): + return "%sp%d" % (self.partition.geom.disk.dev.path[5:], + self.partition.num) + return "%s%d" % (self.partition.geom.disk.dev.path[5:], + self.partition.num) + + def setupDevice(self, chroot, devPrefix='/tmp'): + path = '%d/%s' % (self.getDevice(), devPrefix) + isys.makeDevInode(self.getDevice(), path) + return path + + def solidify(self): + self.device = self.getDevice() + self.partition = None + +class SwapFileDevice(Device): + def __init__(self, file): + Device.__init__(self) + self.device = file + self.device.size = 0 + + def setSize (self, size): + self.size = size + + def setupDevice (self, chroot, devPrefix='/tmp'): + file = os.path.normpath(chroot + self.getDevice()) + if not os.access(file, os.R_OK): + if self.size: + isys.ddfile(file, self.size, None) + else: + raise SystemError, ("swap file creation necessary, but " + "required size is unknown.") + return file + +class LoopbackDevice(Device): + def __init__(self, hostPartition, hostFs): + Device.__init__(self) + self.host = "/dev/" + hostPartition + self.hostfs = hostFs + self.device = "loop1" + + def getComment (self): + return "# LOOP1: %s %s /redhat.img\n" % (self.host, self.hostfs) + +def readFstab (path): + 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 = partitioning.DiskSet() + diskset.openDevices() + labels = diskset.getLabels() + + labelToDevice = {} + for device, label in labels.items(): + labelToDevice[label] = device + + # mark these labels found on the system as used so the factory + # doesn't give them to another device + labelFactory.reserveLabels(labels) + + loopIndex = {} + + f = open (path, "r") + lines = f.readlines () + f.close + + for line in lines: + fields = string.split (line) + + if not fields: continue + + # pick up the magic comment in fstab that tells us which + # device is the loop host in a partionless upgrade + if fields[0] == "#" and len(fields) > 4 and fields[1][:4] == "LOOP": + device = string.lower(fields[1]) + if device[len(device) - 1] == ":": + device = device[:len(device) - 1] + realDevice = fields[2] + if realDevice[:5] == "/dev/": + realDevice = realDevice[5:] + loopIndex[device] = (realDevice, fields[3]) + + if line[0] == "#": + # skip all comments + continue + + # all valid fstab entries have 6 fields + if len (fields) < 4 or len (fields) > 6: continue + + if not fileSystemTypes.has_key(fields[2]): + continue + if string.find(fields[3], "noauto") != -1: continue + + fsystem = fileSystemTypeGet(fields[2]) + label = None + if len(fields) >= 6 and fields[0][:6] == "LABEL=": + label = fields[0][6:] + if labelToDevice.has_key(label): + device = PartitionDevice(labelToDevice[label]) + else: + log ("Warning: fstab file has LABEL=%s, but this label " + "could not be found on any filesystem", label) + # bad luck, skip this entry. + continue + elif (fields[2] == "swap" and fields[0][:5] != "/dev/"): + # swap files + file = fields[0] + + # the loophost looks like /mnt/loophost to the install, not + # like /initrd/loopfs + if file[:15] == "/initrd/loopfs/": + file = "/mnt/loophost/" + file[14:] + + device = SwapFileDevice(file) + elif fields[0][:9] == "/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) + else: + device = PartitionDevice(fields[0][5:]) + + entry = FileSystemSetEntry(device, fields[1], fsystem, fields[3]) + if label: + entry.setLabel(label) + fsset.add(entry) + return fsset + +def isValidExt2(device): + file = '/tmp/' + device + isys.makeDevInode(device, file) + try: + fd = os.open(file, os.O_RDONLY) + except: + return 0 + + buf = os.read(fd, 2048) + os.close(fd) + + if len(buf) != 2048: + return 0 + + if struct.unpack("H", buf[1080:1082]) == (0xef53,): + return 1 + + return 0 + +def ext2FormatFilesystem(argList, messageFile, windowCreator, mntpoint): + if windowCreator: + w = windowCreator(_("Formatting"), + _("Formatting %s filesystem...") % (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) + os.execv(argList[0], argList) + log("failed to exec %s", argList) + sys.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: + l = string.split(num, '/') + w and w.set((int(l[0]) * 100) / int(l[1])) + isys.sync() + num = '' + except OSError, args: + (num, str) = args + if (num != 4): + raise IOError, args + + try: + (pid, status) = os.waitpid(childpid, 0) + except OSError, (errno, msg): + print __name__, "waitpid:", msg + os.close(fd) + + w and w.pop() + + if os.WIFEXITED(status) and (os.WEXITSTATUS(status) == 0): + return 0 + + return 1 + +def enabledSwapDict(): + # returns a dict of swap areas currently being used + f = open("/proc/swaps", "r") + lines = f.readlines() + f.close() + + # the first line is header + lines = lines[1:] + + swaps = {} + for line in lines: + l = string.split(line) + swaps[l[0]] = 1 + + return swaps + +if __name__ == "__main__": + import sys + log.open("foo") + + fsset = readFstab("fstab") + + print fsset.fstab() + print fsset.rootOnLoop() + + sys.exit(0) + fsset = FileSystemSet() + proc = FileSystemSetEntry(Device(), '/proc', 'proc') + fsset.add(proc) + devpts = FileSystemSetEntry(Device(), '/dev/pts', 'devpts', + 'gid=5,mode=620') + fsset.add(devpts) + + device = LoopbackDevice("hda1", "vfat") + mountpoint = FileSystemSetEntry (device, '/') + fsset.add(mountpoint) + + device = SwapFileDevice("/SWAP") + mountpoint = FileSystemSetEntry (device, "swap", "swap") + fsset.add(mountpoint) + + print fsset.fstab() diff --git a/partitioning.py b/partitioning.py new file mode 100644 index 000000000..57809719f --- /dev/null +++ b/partitioning.py @@ -0,0 +1,561 @@ +# +# partitioning.py: partitioning and other disk management +# +# Matt Wilson <msw@redhat.com> +# +# Copyright 2001 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. +# + +if __name__ == "__main__": + import sys + sys.path.append ("isys") + sys.path.append ("balkan") + +import isys +import parted +import math + +from fsset import * +from translate import _ + +# different types of partition requests +# REQUEST_PREEXIST is a placeholder for a pre-existing partition on the system +# REQUEST_NEW is a request for a partition which will be automatically +# created based on various constraints on size, drive, etc +# +REQUEST_PREEXIST = 1 +REQUEST_NEW = 2 +REQUEST_RAID = 4 + +fsTypes = {} + +fs_type = parted.file_system_type_get_next () +while fs_type: + fsTypes[fs_type.name] = fs_type + fs_type = parted.file_system_type_get_next (fs_type) + +class PartitioningError: + def __init__ (self, value): + self.value = value + + def __str__ (self): + return self.value + +def get_flags (part): + string="" + if not part.is_active (): + return string + first=1 + flag = parted.partition_flag_next (0) + while flag: + if part.get_flag (flag): + string = string + parted.partition_flag_get_name (flag) + if first: + first = 0 + else: + string = string + ", " + flag = parted.partition_flag_next (flag) + return string + +def start_sector_to_cyl(device, sector): + return int(math.floor((float(sector) + / (device.heads * device.sectors)) + 1)) + +def end_sector_to_cyl(device, sector): + return int(math.ceil(float((sector + 1)) + / (device.heads * device.sectors))) + +def start_cyl_to_sector(device, cyl): + return long((cyl - 1) * (device.heads * device.sectors)) + +def end_cyl_to_sector(device, cyl): + return long(((cyl) * (device.heads * device.sectors)) - 1) + +def getPartSize(partition): + return partition.geom.length * partition.geom.disk.dev.sector_size / 1024.0 / 1024.0 + +def get_partition_name(partition): + if (partition.geom.disk.dev.type == parted.DEVICE_DAC960 + or partition.geom.disk.dev.type == parted.DEVICE_CPQARRAY): + return "%sp%d" % (partition.geom.disk.dev.path[5:], + partition.num) + return "%s%d" % (partition.geom.disk.dev.path[5:], + partition.num) + +def get_logical_partitions(disk): + rc = [] + part = disk.next_partition () + while part: + if part.type & parted.PARTITION_LOGICAL: + rc.append(part) + part = disk.next_partition (part) + + return rc + +def get_primary_partitions(disk): + rc = [] + part = disk.next_partition() + while part: + if part.type == parted.PARTITION_PRIMARY: + rc.append(part) + part = disk.next_partition(part) + + return rc + + +def get_raid_partitions(disk): + rc = [] + part = disk.next_partition() + while part: + + if part.type & (parted.PARTITION_METADATA | parted.PARTITION_FREESPACE | parted.PARTITION_EXTENDED): + part = disk.next_partition(part) + continue + + if part.get_flag(parted.PARTITION_RAID) == 1: + rc.append(part) + part = disk.next_partition(part) + + return rc + + +# returns error string if something not right about request +# returns error string if something not right about request +def sanityCheck(reqpartitions, newrequest): + # see if mount point is valid if its a new partition request + + mustbeonroot = ['/bin','/dev','/sbin','/etc','/lib','/root','/mnt'] + mustbeonlinuxfs = ['/', '/boot', '/var', '/tmp', '/usr', '/home'] + + mntpt = newrequest.mountpoint + fstype = newrequest.fstype + + if mntpt: + passed = 1 + if not mntpt: + passed = 0 + else: + if mntpt[0] != '/' or (len(mntpt) > 1 and mntpt[-1:] == '/'): + passed = 0 + + if not passed: + return _("The mount point is invalid. Mount points must start " + "with '/' and cannot end with '/', and must contain " + "printable characters.") + else: + if newrequest.fstype.isMountable() and newrequest.type == REQUEST_NEW: + return _("Please specify a mount point for this partition.") + else: + # its an existing partition so don't force a mount point + return + + # mount point is defined and is legal. now make sure its unique + if reqpartitions and reqpartitions.requests: + for request in reqpartitions.requests: + if request.mountpoint == mntpt and request.start != newrequest.start: + return _("The mount point %s is already in use, please " + "choose a different mount point." % (mntpt)) + + + # further sanity checks + if fstype.isLinuxNativeFS(): + if mntpt in mustbeonroot: + return _("This mount point is invalid. This directory must " + "be on the / filesystem.") + + if fstype.getName() == "linux-swap": + if newrequest.size * 1024 > MAX_SWAP_PART_SIZE_KB: + return _("This swap partition exceeds the maximum size of " + "%s MB.") % (MAX_SWAP_PART_SIZE_KB / 1024) + else: + if mntpt in mustbeonlinuxfs: + return _("This mount point must be on a linux filesystem.") + + return None + +class DeleteSpec: + def __init__(self, drive, start, end): + self.drive = drive + self.start = start + self.end = end + + +class PartitionSpec: + def __init__(self, fstype, requesttype = REQUEST_NEW, + size = None, grow = 0, maxSize = 0, + mountpoint = None, + start = None, end = None, partnum = None, + drive = None, primary = None, secondary = None, + format = None, options = None, + constraint = None, + raidmembers = None, raidlevel = None, + raidspares = None): + # + # requesttype: REQUEST_PREEXIST or REQUEST_NEW or REQUEST_RAID + # + # XXX: unenforced requirements for a partition spec + # must have (size) || (start && end) + # fs_type, mountpoint + # if partnum, require drive + self.type = requesttype + self.fstype = fstype + self.size = size + self.grow = grow + self.maxSize = maxSize + self.mountpoint = mountpoint + self.start = start + self.end = end + self.partnum = partnum + self.drive = drive + self.primary = primary + self.secondary = secondary + self.format = format + self.options = options + self.constraint = constraint + self.partition = None + self.requestSize = size + self.raidmembers = raidmembers + self.raidlevel = raidlevel + self.raidspares = raidspares + + # device is what we currently think the device is + # realDevice is used by partitions which are pre-existing + self.device = None + self.realDevice = None + + # there has to be a way to go from device -> drive... but for now + self.currentDrive = None + + def __str__(self): + return "mountpoint: %s type: %s\n" %(self.mountpoint, self.fstype.getName()) +\ + " size: %sM requestSize: %sM grow: %s max: %s\n" %(self.size, self.requestSize, self.grow, self.maxSize) +\ + " start: %s end: %s partnum: %s\n" %(self.start, self.end, self.partnum) +\ + " drive: %s primary: %s secondary: %s\n" %(self.drive, self.primary, self.secondary) +\ + " format: %s, options: %s" %(self.format, self.options) +\ + " device: %s, realDevice: %s" %(self.device, self.realDevice)+\ + " raidlevel: %s" % (self.raidlevel)+\ + " raidspares: %s" % (self.raidspares) + + # turn a partition request into a fsset entry + def toEntry(self): + if self.type == REQUEST_RAID: + device = RAIDDevice(int(self.raidlevel[-1:]), self.raidmembers, + spares = self.raidspares) + else: + device = PartitionDevice(self.device) + + # 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 = FileSystemSetEntry(device, mountpoint, self.fstype) + if self.format: + entry.setFormat(self.format) + return entry + +class PartitionRequests: + def __init__ (self, diskset = None): + self.requests = [] + self.deletes = [] + if diskset: + self.setFromDisk(diskset) + + def setFromDisk(self, diskset): + self.deletes = [] + diskset.refreshDevices() + drives = diskset.disks.keys() + drives.sort() + for drive in drives: + disk = diskset.disks[drive] + part = disk.next_partition() + while part: + if part.type & parted.PARTITION_METADATA: + part = disk.next_partition(part) + continue + + if part.type & parted.PARTITION_FREESPACE: + ptype = None + elif part.type & parted.PARTITION_EXTENDED: + ptype = None + elif part.fs_type: + if part.fs_type.name == "linux-swap": + ptype = fileSystemTypeGet("swap") + elif part.fs_type.name == "FAT": + ptype = fileSystemTypeGet("vfat") + else: + try: + ptype = fileSystemTypeGet(part.fs_type.name) + except: + ptype = fileSystemTypeGet("foreign") + else: + ptype = None + + start = part.geom.start + end = part.geom.end + size = getPartSize(part) + + spec = PartitionSpec(ptype, requesttype = REQUEST_PREEXIST, + start = start, end = end, size = size) + spec.device = PartedPartitionDevice(part).getDevice() + self.addRequest(spec) + part = disk.next_partition(part) + + def addRequest (self, request): + self.requests.append(request) + self.requests.sort() + + def addDelete (self, delete): + self.deletes.append(delete) + self.deletes.sort() + + def removeRequest (self, request): + self.requests.remove(request) + + def getRequestByMountPoint(self, mount): + for request in self.requests: + if request.mountpoint == mount: + return request + return None + + def getRequestByDeviceName(self, device): + for request in self.requests: + if request.device == device: + return request + return None + + def sortRequests(self): + n = 0 + while n < len(self.requests): + for request in self.requests: + if request.size < self.requests[n].size: + tmp = self.requests[n] + index = self.requests.index(request) + self.requests[n] = request + self.requests[index] = tmp + n = n + 1 + + def copy (self): + new = PartitionRequests() + for request in self.requests: + new.addRequest(request) + for delete in self.deletes: + new.addDelete(delete) + return new + + +class DiskSet: + def __init__ (self): + self.disks = {} + + def getLabels(self): + labels = {} + + drives = self.disks.keys() + drives.sort + + for drive in drives: + disk = self.disks[drive] + part = disk.next_partition () + while part: + if part.fs_type and (part.fs_type.name == "ext2" + or part.fs_type.name == "ext3"): + node = get_partition_name(part) + label = isys.readExt2Label(node) + if label: + labels[node] = label + part = disk.next_partition(part) + + return labels + + def driveList (self): + drives = isys.hardDriveDict().keys() + drives.sort (isys.compareDrives) + return drives + + def drivesByName (self): + return isys.hardDriveDict() + + def addPartition (self, device, type, spec): + if not self.disks.has_key (device): + raise PartitioningError, ("unknown device passed to " + "addPartition: %s" % (device,)) + disk = self.disks[device] + + part = disk.next_partition () + status = 0 + while part: + if (part.type == parted.PARTITION_FREESPACE + and part.geom.length >= spec.size): + newp = disk.partition_new (type, spec.fs_type, + part.geom.start, + part.geom.start + spec.size) + constraint = disk.constraint_any () + try: + disk.add_partition (newp, constraint) + status = 1 + break + except parted.error, msg: + raise PartitioningError, msg + part = disk.next_partition (part) + if not status: + raise PartitioningError, ("Not enough free space on %s to create " + "new partition" % (device,)) + return newp + + def deleteAllPartitions (self): + for disk in self.disks.values(): + disk.delete_all () + + def savePartitions (self): + for disk in self.disks.values(): + disk.write() + del disk + self.refreshDevices() + + def refreshDevices (self): + self.disks = {} + self.openDevices() + + def openDevices (self): + if self.disks: + return + for drive in self.driveList (): + isys.makeDevInode(drive, '/tmp/' + drive) + try: + dev = parted.PedDevice.get ('/tmp/' + drive) + except parted.error, msg: + raise PartitioningError, msg + try: + disk = parted.PedDisk.open(dev) + self.disks[drive] = disk + except parted.error, msg: + raise PartitioningError, msg + + def partitionTypes (self): + rc = [] + drives = self.disks.keys() + drives.sort + + for drive in drives: + disk = self.disks[drive] + part = disk.next_partition () + while part: + if part.type in (parted.PARTITION_PRIMARY, + parted.PARTITION_LOGICAL): + device = get_partition_name(part) + if part.fs_type: + ptype = part.fs_type.name + else: + ptype = None + rc.append (device, ptype) + part = disk.next_partition (part) + + return rc + + def diskState (self): + rc = "" + for disk in self.disks.values(): + rc = rc + ("%s: %s length %ld, maximum " + "primary partitions: %d\n" % + (disk.dev.path, + disk.dev.model, + disk.dev.length, + disk.max_primary_partition_count)) + + part = disk.next_partition() + if part: + rc = rc + ("Device Type Filesystem Start " + "End Length Flags\n") + rc = rc + ("------ ---- ---------- ----- " + "--- ------ -----\n") + while part: + if not part.type & parted.PARTITION_METADATA: + device = "" + fs_type_name = "" + if part.num > 0: + device = get_partition_name(part) + if part.fs_type: + fs_type_name = part.fs_type.name + flags = get_flags (part) + rc = rc + ("%-9s %-12s %-12s %-10ld %-10ld %-10ld %7s\n" + % (device, part.type_name, fs_type_name, + part.geom.start, part.geom.end, part.geom.length, + flags)) + part = disk.next_partition(part) + return rc + +def AutoPartition (fsset, diskset): + from fsset import * + diskset.deleteAllPartitions () + + spec = PartitionSpec (fsTypes['linux-swap'], (64L * 2048L)) + diskset.addPartition (diskset.driveList()[0], + parted.PARTITION_PRIMARY, spec) + + spec = PartitionSpec (fsTypes['ext2'], (1024L * 2048L)) + diskset.addPartition (diskset.driveList()[0], + parted.PARTITION_PRIMARY, spec) + + device = PartitionDevice (diskset.driveList()[0] + "1") + mountpoint = FileSystemSetEntry (device, 'swap', 'swap') + mountpoint.setFormat(1) + fsset.add(mountpoint) + + device = PartitionDevice (diskset.driveList()[0] + "2") + mountpoint = FileSystemSetEntry (device, '/') + mountpoint.setFormat(1) + fsset.add(mountpoint) + + from log import log + log (diskset.diskState()) + +if __name__ == "__main__": + foo = DiskSet() + foo.deleteAllPartitions () + print foo.diskState () + print '---------------------' + spec = PartitionSpec (fsTypes['ext2'], 12060L) + foo.addPartition ("sda", parted.PARTITION_PRIMARY, spec) + print foo.diskState () + spec = PartitionSpec (fsTypes['ext2'], 16060L) + foo.addPartition ("sda", parted.PARTITION_PRIMARY, spec) + print foo.diskState () + + spec = PartitionSpec (fsTypes['ext2'], 16060L) + foo.addPartition ("sda", parted.PARTITION_PRIMARY, spec) + print foo.diskState () + + spec = PartitionSpec (fsTypes['ext2'], 16060L) + foo.addPartition ("sda", parted.PARTITION_PRIMARY, spec) + print foo.diskState () + + spec = PartitionSpec (fsTypes['ext2'], 16060L) + foo.addPartition ("sda", parted.PARTITION_PRIMARY, spec) + print foo.diskState () + + spec = PartitionSpec (fsTypes['ext2'], 16060L) + foo.addPartition ("sda", parted.PARTITION_PRIMARY, spec) + print foo.diskState () + + spec = PartitionSpec (fsTypes['ext2'], 16060L) + foo.addPartition ("sda", parted.PARTITION_PRIMARY, spec) + print foo.diskState () + + spec = PartitionSpec (fsTypes['ext2'], 16060L) + foo.addPartition ("sda", parted.PARTITION_PRIMARY, spec) + print foo.diskState () + + spec = PartitionSpec (fsTypes['ext2'], 16060L) + foo.addPartition ("sda", parted.PARTITION_PRIMARY, spec) + print foo.diskState () |