# # upgrade.py - Existing install probe and upgrade procedure # # 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 # import isys import os import iutil import time import sys import os.path import string import selinux from flags import flags from constants import * from product import productName from storage import findExistingRootDevices, FSSet from storage.formats import getFormat import rhpl import rhpl.arch import gettext _ = lambda x: gettext.ldgettext("anaconda", x) import rpm import logging log = logging.getLogger("anaconda") def guessGuestArch(rootdir): """root path -> None|"architecture" Guess the architecture of installed system """ ts = rpm.ts(rootdir) packages = ["filesystem", "initscripts"] #get information from packages for pkg in packages: try: mi=ts.dbMatch("name",pkg) for hdr in mi: return hdr["arch"] except: pass return None def isUpgradingArch(anaconda): """anaconda -> (bool, oldarch) Check if the upgrade should change architecture of instalation""" try: rpmplatform = open(anaconda.rootPath+"/etc/rpm/platform").readline().strip() rpmarch = rpmplatform[:rpmplatform.index("-")] return rhpl.arch.canonArch!=rpmarch, rpmarch except IOError: #try some fallback methods rpmarch = guessGuestArch(anaconda.rootPath) if rpmarch: return rhpl.arch.canonArch!=rpmarch, rpmarch else: return False, "unknown" def queryUpgradeArch(anaconda): archupgrade, oldrpmarch = isUpgradingArch(anaconda) #Check if we are to upgrade the architecture of previous product if anaconda.dir == DISPATCH_FORWARD or not archupgrade: return DISPATCH_FORWARD rc = anaconda.intf.messageWindow(_("Proceed with upgrade?"), _("You have choosen the upgrade for %s " "architecture, but the installed system " "is for %s architecture. " "\n\n" % (rhpl.arch.canonArch, oldrpmarch,)) + _("Would you like to upgrade " " the installed system to the %s architecture?" % (rhpl.arch.canonArch,)), type="custom", custom_icon=["error","error"], custom_buttons=[_("_Exit installer"), _("_Continue")]) if rc == 0: sys.exit(0) flags.updateRpmPlatform = True return DISPATCH_FORWARD def queryUpgradeContinue(anaconda): if anaconda.dir == DISPATCH_FORWARD: return rc = anaconda.intf.messageWindow(_("Proceed with upgrade?"), _("The file systems of the Linux installation " "you have chosen to upgrade have already been " "mounted. You cannot go back past this point. " "\n\n") + _("Would you like to continue with the upgrade?"), type="custom", custom_icon=["error","error"], custom_buttons=[_("_Exit installer"), _("_Continue")]) if rc == 0: sys.exit(0) return DISPATCH_FORWARD def findRootParts(anaconda): if anaconda.dir == DISPATCH_BACK: return if anaconda.id.rootParts is None: anaconda.id.rootParts = findExistingRoots(anaconda) root_device = None # ks.cfg can pass device as raw device, label or uuid. no quotes allowed if anaconda.isKickstart and anaconda.id.ksdata.upgrade.root_device is not None: root_device=anaconda.id.ksdata.upgrade.root_device anaconda.id.upgradeRoot = [] 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) if productName.find("Red Hat Enterprise Linux") == -1: anaconda.dispatch.skipStep("installtype", skip = 1) else: anaconda.dispatch.skipStep("findinstall", skip = 1) anaconda.dispatch.skipStep("installtype", skip = 0) def findExistingRoots(anaconda, upgradeany = 0): 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, "")] return [] return rootparts def bindMountDevDirectory(instPath): getFormat("bind", device="/dev", mountpoint="/dev", exists=True).mount(chroot=instPath) # returns None if no filesystem exist to migrate def upgradeMigrateFind(anaconda): migents = anaconda.id.fsset.getMigratableEntries() if not migents or len(migents) < 1: anaconda.dispatch.skipStep("upgrademigratefs") else: anaconda.dispatch.skipStep("upgrademigratefs", skip = 0) # returns None if no more swap is needed def upgradeSwapSuggestion(anaconda): # mem is in kb -- round it up to the nearest 4Mb mem = iutil.memInstalled() rem = mem % 16384 if rem: mem = mem + (16384 - rem) mem = mem / 1024 anaconda.dispatch.skipStep("addswap", 0) # don't do this if we have more then 250 MB if mem > 250: anaconda.dispatch.skipStep("addswap", 1) return swap = iutil.swapAmount() / 1024 # if we have twice as much swap as ram and at least 192 megs # total, we're safe if (swap >= (mem * 1.5)) and (swap + mem >= 192): anaconda.dispatch.skipStep("addswap", 1) return # if our total is 512 megs or more, we should be safe if (swap + mem >= 512): anaconda.dispatch.skipStep("addswap", 1) return fsList = [] 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 + device.format.mountpoint) if space > 16: info = (device, space) fsList.append(info) suggestion = mem * 2 - swap if (swap + mem + suggestion) < 192: suggestion = 192 - (swap + mem) if suggestion < 32: suggestion = 32 suggSize = 0 suggMnt = None for (device, size) in fsList: if (size > suggSize) and (size > (suggestion + 100)): suggDev = device anaconda.id.upgradeSwapInfo = (fsList, suggestion, suggDev) # XXX handle going backwards def upgradeMountFilesystems(anaconda): # mount everything and turn on swap if flags.setupFilesystems: try: 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) checkLinks = ( '/etc', '/var', '/var/lib', '/var/lib/rpm', '/boot', '/tmp', '/var/tmp', '/root', '/bin/sh', '/usr/tmp') badLinks = [] for n in checkLinks: if not os.path.islink(anaconda.rootPath + n): continue l = os.readlink(anaconda.rootPath + n) if l[0] == '/': badLinks.append(n) if badLinks: message = _("The following files are absolute symbolic " "links, which we do not support during an " "upgrade. Please change them to relative " "symbolic links and restart the upgrade.\n\n") for n in badLinks: message = message + '\t' + n + '\n' anaconda.intf.messageWindow(_("Absolute Symlinks"), message) sys.exit(0) # fix for 80446 badLinks = [] mustBeLinks = ( '/usr/tmp', ) for n in mustBeLinks: if not os.path.islink(anaconda.rootPath + n): badLinks.append(n) if badLinks: message = _("The following are directories which should instead " "be symbolic links, which will cause problems with the " "upgrade. Please return them to their original state " "as symbolic links and restart the upgrade.\n\n") for n in badLinks: 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"), _("%s not found") % (anaconda.rootPath + "/etc/fstab",), type="ok") return DISPATCH_BACK anaconda.id.storage.fsset.parseFSTab(chroot=anaconda.rootPath) if flags.setupFilesystems: 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) try: if os.path.exists(anaconda.rootPath + "/.autorelabel"): ctx = selinux.getfilecon(anaconda.rootPath + "/.autorelabel")[1] if not ctx or ctx == "unlabeled": flags.selinux = False log.info("Disabled SELinux for upgrade based on /.autorelabel") except Exception, e: log.warning("error checking selinux state: %s" %(e,)) def setSteps(anaconda): dispatch = anaconda.dispatch dispatch.setStepList( "language", "keyboard", "welcome", "installtype", "storageinit", "findrootparts", "findinstall", "upgrademount", "upgrademigfind", "upgrademigratefs", "upgradearchitecture", "upgradecontinue", "reposetup", "upgbootloader", "checkdeps", "dependencies", "confirmupgrade", "postselection", "install", "preinstallconfig", "installpackages", "postinstallconfig", "instbootloader", "dopostaction", "methodcomplete", "postscripts", "copylogs", "complete" ) if not iutil.isX86(): dispatch.skipStep("bootloader") if not iutil.isX86(): dispatch.skipStep("upgbootloader")