diff options
author | Jeremy Katz <katzj@redhat.com> | 2007-01-18 18:29:00 +0000 |
---|---|---|
committer | Jeremy Katz <katzj@redhat.com> | 2007-01-18 18:29:00 +0000 |
commit | 4a7d5899d75bb5c59216af8e963534e48c932c77 (patch) | |
tree | 68d0f359fe81a4dafaeec204ac490cdfd2d46d70 | |
parent | 13bb088ffbff42cda9b2b7b1acdf34f2df58a3a1 (diff) | |
download | anaconda-4a7d5899d75bb5c59216af8e963534e48c932c77.tar.gz anaconda-4a7d5899d75bb5c59216af8e963534e48c932c77.tar.xz anaconda-4a7d5899d75bb5c59216af8e963534e48c932c77.zip |
2007-01-18 Jeremy Katz <katzj@redhat.com>
* syslogd.py (InstSyslog.start): Ensure that we have a syslogd or
don't try to start. Otherwise, we end up hanging things
* yuminstall.py (YumBackend.__init__): yum installs can do package
selection
* livecd.py: Add live CD image copy backend and install method.
* installclasses/fedora.py: Handle installclass API changes; allow
livecd method
* installclass.py (BaseInstallClass.setSteps): Take anaconda
instead of dispatch as an argument; use so that the backend
upgrade allowing + package selection can work
(BaseInstallClass.setDefaultPartitioning): Make it easier to
default to no-LVM
* kickstart.py (Kickstart.setSteps): Tweak for calling change
* upgradeclass.py (InstallClass.setSteps): Likewise.
* installclasses/rhel.py (InstallClass.setSteps): Likewise.
* anaconda: Pass anaconda to instClass.setSteps; don't depend on
the existence of /mnt/runtime/... with x_already_set
* backend.py (AnacondaBackend.__init__): Let the backend define
whether or not upgrades are supported
(AnacondaBackend.doPreInstall): postInstall kills the log, start
it in the pre
(AnacondaBackend.initLog): Ensure the dir we want to use exists
(writeConfiguration): Write instdata before backend bits
-rw-r--r-- | ChangeLog | 31 | ||||
-rwxr-xr-x | anaconda | 4 | ||||
-rw-r--r-- | backend.py | 14 | ||||
-rw-r--r-- | installclass.py | 19 | ||||
-rw-r--r-- | installclasses/fedora.py | 12 | ||||
-rw-r--r-- | installclasses/rhel.py | 3 | ||||
-rw-r--r-- | kickstart.py | 7 | ||||
-rw-r--r-- | livecd.py | 269 | ||||
-rw-r--r-- | syslogd.py | 6 | ||||
-rw-r--r-- | upgradeclass.py | 3 | ||||
-rw-r--r-- | yuminstall.py | 1 |
11 files changed, 350 insertions, 19 deletions
@@ -1,3 +1,34 @@ +2007-01-18 Jeremy Katz <katzj@redhat.com> + + * syslogd.py (InstSyslog.start): Ensure that we have a syslogd or + don't try to start. Otherwise, we end up hanging things + + * yuminstall.py (YumBackend.__init__): yum installs can do package + selection + + * livecd.py: Add live CD image copy backend and install method. + + * installclasses/fedora.py: Handle installclass API changes; allow + livecd method + + * installclass.py (BaseInstallClass.setSteps): Take anaconda + instead of dispatch as an argument; use so that the backend + upgrade allowing + package selection can work + (BaseInstallClass.setDefaultPartitioning): Make it easier to + default to no-LVM + * kickstart.py (Kickstart.setSteps): Tweak for calling change + * upgradeclass.py (InstallClass.setSteps): Likewise. + * installclasses/rhel.py (InstallClass.setSteps): Likewise. + * anaconda: Pass anaconda to instClass.setSteps; don't depend on + the existence of /mnt/runtime/... with x_already_set + + * backend.py (AnacondaBackend.__init__): Let the backend define + whether or not upgrades are supported + (AnacondaBackend.doPreInstall): postInstall kills the log, start + it in the pre + (AnacondaBackend.initLog): Ensure the dir we want to use exists + (writeConfiguration): Write instdata before backend bits + 2007-01-18 Chris Lumens <clumens@redhat.com> * loader2/net.c (setKickstartNetwork): Bring up loopback as well @@ -788,7 +788,7 @@ if __name__ == "__main__": # now determine if we're going to run in GUI or TUI mode # # if no X server, we have to use text mode - if not (flags.test or flags.rootpath) and (rhpl.getArch() != "s390" and not os.access("/mnt/runtime/usr/bin/Xorg", os.X_OK)): + if not (flags.test or flags.rootpath or x_already_set) and (rhpl.getArch() != "s390" and not os.access("/mnt/runtime/usr/bin/Xorg", os.X_OK)): stdoutLog.warning(_("Graphical installation not available... " "Starting text mode.")) time.sleep(2) @@ -945,7 +945,7 @@ if __name__ == "__main__": anaconda.id.setHeadless(opts.isHeadless) instClass.setAsHeadless(anaconda.dispatch, opts.isHeadless) - instClass.setSteps(anaconda.dispatch) + instClass.setSteps(anaconda) # comment out the next line to make exceptions non-fatal sys.excepthook = lambda type, value, tb, anaconda=anaconda: handleException(anaconda, (type, value, tb)) diff --git a/backend.py b/backend.py index a4bca125c..50a90e211 100644 --- a/backend.py +++ b/backend.py @@ -37,6 +37,10 @@ class AnacondaBackend: self.instLog = None self.modeText = "" + # some backends may not support upgrading + self.supportsUpgrades = True + self.supportsPackageSelection = False + def doPreSelection(self, intf, id, instPath): pass @@ -44,7 +48,7 @@ class AnacondaBackend: pass def doPreInstall(self, anaconda): - pass + self.initLog(anaconda.id, anaconda.rootPath) def doPostInstall(self, anaconda): sys.stdout.flush() @@ -52,11 +56,15 @@ class AnacondaBackend: syslog.stop() def doInstall(self, anaconda): + log.warning("doInstall not implemented for backend!") pass def initLog(self, id, instPath): upgrade = id.getUpgrade() + if not os.path.isdir(instPath + "/root"): + iutil.mkdirChain(instPath + "/root") + if upgrade: logname = '/root/upgrade.log' else: @@ -99,7 +107,7 @@ class AnacondaBackend: self.modeText = _("Installing %s\n") def kernelVersionList(self): - pass + return [] def doInitialSetup(self, anaconda): pass @@ -174,6 +182,6 @@ def doBasePackageSelect(anaconda): def writeConfiguration(anaconda): log.info("Writing main configuration") if not flags.test: - anaconda.backend.writeConfiguration() anaconda.id.write(anaconda) + anaconda.backend.writeConfiguration() diff --git a/installclass.py b/installclass.py index dcb41bb60..d3176ddc4 100644 --- a/installclass.py +++ b/installclass.py @@ -42,7 +42,7 @@ class BaseInstallClass: name = "base" pkgstext = "" # default to showing the upgrade option - showUpgrade = 1 # FIXME: no upgrade for now while doing yum work + showUpgrade = True # list of of (txt, grplist) tuples for task selection screen tasks = [] @@ -107,7 +107,8 @@ class BaseInstallClass: if initAll: id.partitions.reinitializeDisks = initAll - def setSteps(self, dispatch): + def setSteps(self, anaconda): + dispatch = anaconda.dispatch dispatch.setStepList( "language", "keyboard", @@ -156,8 +157,12 @@ class BaseInstallClass: if rhpl.getArch() != "i386" and rhpl.getArch() != "x86_64": dispatch.skipStep("bootloader", permanent=1) + # allow backends to disable interactive package selection + if not anaconda.backend.supportsPackageSelection: + dispatch.skipStep("tasksel", skip = 1) + # allow install classes to turn off the upgrade - if self.showUpgrade == 0: + if not self.showUpgrade or not anaconda.backend.supportsUpgrades: dispatch.skipStep("findrootparts", skip = 1) # 'noupgrade' can be used on the command line to force not looking @@ -431,7 +436,7 @@ class BaseInstallClass: return AnacondaBackend def setDefaultPartitioning(self, partitions, clear = CLEARPART_TYPE_LINUX, - doClear = 1): + doClear = 1, useLVM = True): autorequests = [ ("/", None, 1024, None, 1, 1, 1) ] bootreq = getAutopartitionBoot() @@ -444,7 +449,11 @@ class BaseInstallClass: if doClear: partitions.autoClearPartType = clear partitions.autoClearPartDrives = [] - partitions.autoPartitionRequests = autoCreateLVMPartitionRequests(autorequests) + + if useLVM: + partitions.autoPartitionRequests = autoCreateLVMPartitionRequests(autorequests) + else: + partitions.autoPartitionRequests = autoCreatePartitionRequests(autorequests) def setInstallData(self, anaconda): diff --git a/installclasses/fedora.py b/installclasses/fedora.py index d069dc338..94b2c563e 100644 --- a/installclasses/fedora.py +++ b/installclasses/fedora.py @@ -40,14 +40,20 @@ class InstallClass(BaseInstallClass): grps = anaconda.backend.getDefaultGroups(anaconda) map(lambda x: anaconda.backend.selectGroup(x), grps) - def setSteps(self, dispatch): - BaseInstallClass.setSteps(self, dispatch); - dispatch.skipStep("partition") + def setSteps(self, anaconda): + BaseInstallClass.setSteps(self, anaconda); + anaconda.dispatch.skipStep("partition") def getMethod(self, methodstr): + if methodstr.startswith("livecd://"): + import livecd + return livecd.LiveCDImageMethod return BaseInstallClass.getMethod(self, methodstr) def getBackend(self, methodstr): + if methodstr.startswith("livecd://"): + import livecd + return livecd.LiveCDCopyBackend return yuminstall.YumBackend def __init__(self, expert): diff --git a/installclasses/rhel.py b/installclasses/rhel.py index 4f89031c6..fa20c31d9 100644 --- a/installclasses/rhel.py +++ b/installclasses/rhel.py @@ -36,7 +36,8 @@ class InstallClass(BaseInstallClass): grps = anaconda.backend.getDefaultGroups(anaconda) map(lambda x: anaconda.backend.selectGroup(x), grps) - def setSteps(self, dispatch): + def setSteps(self, anaconda): + dispatch = anaconda.dispatch BaseInstallClass.setSteps(self, dispatch); dispatch.skipStep("partition") dispatch.skipStep("regkey", skip = 0) diff --git a/kickstart.py b/kickstart.py index 998513feb..4bce10093 100644 --- a/kickstart.py +++ b/kickstart.py @@ -847,11 +847,12 @@ class Kickstart(cobject): len(self.handler.packages.packageList) > 0 or \ len(self.handler.packages.excludedList) > 0 - def setSteps(self, dispatch): + def setSteps(self, anaconda): + dispatch = anaconda.dispatch if self.handler.upgrade.upgrade: from upgradeclass import InstallClass theUpgradeclass = InstallClass(0) - theUpgradeclass.setSteps(dispatch) + theUpgradeclass.setSteps(anaconda) # we have no way to specify migrating yet dispatch.skipStep("upgrademigfind") @@ -863,7 +864,7 @@ class Kickstart(cobject): dispatch.skipStep("betanag") dispatch.skipStep("installtype") else: - cobject.setSteps(self, dispatch) + cobject.setSteps(self, anaconda) dispatch.skipStep("findrootparts") if self.handler.interactive.interactive or flags.autostep: diff --git a/livecd.py b/livecd.py new file mode 100644 index 000000000..cf25320b3 --- /dev/null +++ b/livecd.py @@ -0,0 +1,269 @@ +# +# An anaconda backend to do an install from a live CD image +# +# The basic idea is that with a live CD, we already have an install +# and should be able to just copy those bits over to the disk. So we dd +# the image, move things to the "right" filesystem as needed, and then +# resize the rootfs to the size of its container. +# +# Copyright 2007 Red Hat, Inc. +# Jeremy Katz <katzj@redhat.com> +# +# This software may be freely redistributed under the terms of the GNU +# general public license. +# +# 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., 675 Mass Ave, Cambridge, MA 02139, USA. +# + +import os, sys +import stat +import shutil + +from rhpl.translate import _, N_ + +from flags import flags +from constants import * + +import backend +import installmethod +import isys +import iutil + +import packages + +import logging +log = logging.getLogger("anaconda") + +def copytree(src, dst, symlinks=False): + # copy of shutil.copytree which doesn't require dst to not exist + names = os.listdir(src) + if not os.path.isdir(dst): + os.makedirs(dst) + errors = [] + for name in names: + srcname = os.path.join(src, name) + dstname = os.path.join(dst, name) + try: + if symlinks and os.path.islink(srcname): + linkto = os.readlink(srcname) + os.symlink(linkto, dstname) + elif os.path.isdir(srcname): + copytree(srcname, dstname, symlinks) + else: + shutil.copy2(srcname, dstname) + # XXX What about devices, sockets etc.? + except (IOError, os.error), why: + errors.append((srcname, dstname, str(why))) + # catch the Error from the recursive copytree so that we can + # continue with other files + except Error, err: + errors.extend(err.args[0]) + try: + shutil.copystat(src, dst) + except OSError, why: + errors.extend((src, dst, str(why))) + if errors: + raise Error, errors + +class LiveCDImageMethod(installmethod.InstallMethod): + def __init__(self, method, rootpath, intf): + """@param method livecd://mountedlocation """ + installmethod.InstallMethod.__init__(self, method, rootpath, intf) + + self.cdmntpt = method[8:] + if not os.path.exists("%s/squashfs.img" %(self.cdmntpt,)): + intf.messageWindow(_("Unable to find image"), + _("The given location isn't a valid %s " + "live CD to use as an installation source.") + %(productName,), type = "custom", + custom_icon="error", + custom_buttons=[_("Exit installer")]) + sys.exit(0) + + def getLiveCDMountPoint(self): + return self.cdmntpt + + +class LiveCDCopyBackend(backend.AnacondaBackend): + def __init__(self, method, instPath): + backend.AnacondaBackend.__init__(self, method, instPath) + self.supportsUpgrades = False + self.supportsPackageSelection = False + + def doPreInstall(self, anaconda): + if anaconda.dir == DISPATCH_BACK: + for d in ("/selinux", "/dev"): + try: + isys.umount(anaconda.rootPath + d, removeDir = 0) + except Exception, e: + log.error("unable to unmount %s: %s" %(d, e)) + return + + anaconda.id.fsset.umountFilesystems(anaconda.rootPath, swapoff = False) + + def doInstall(self, anaconda): + log.info("Preparing to install packages") + if flags.test: + log.info("Test mode - not performing install") + return + + progress = anaconda.id.instProgress + progress.processEvents() + + osimg = "/mnt/installer/squashed/os.img" # the real image + osfd = os.open(osimg, os.O_RDONLY) + + r = anaconda.id.fsset.getEntryByMountPoint("/") + rootfs = r.device.getDevice() + rootfd = os.open("/dev/" + rootfs, os.O_WRONLY) + + readamt = 1024 * 1024 * 8 # 8 megs at a time + size = float(os.stat(osimg)[stat.ST_SIZE]) + copied = 0 + while copied < size: + buf = os.read(osfd, readamt) + written = os.write(rootfd, buf) + if (written < readamt) and (written < len(buf)): + raise RuntimeError, "error copying filesystem!" + copied += written + progress.completePackage(pct = copied / size) + progress.processEvents() + + os.close(osfd) + os.close(rootfd) + + # unset-up the image + isys.umount("/mnt/installer/squashed") + isys.unlosetup("/dev/loop4") + anaconda.id.instProgress = None + + def _doFilesystemMangling(self, anaconda): + log.info("doing post-install fs mangling") + wait = anaconda.intf.waitWindow(_("Doing post-installation"), + _("Performing post-installation filesystem changes. This may take several minutes...")) + + # remount filesystems + anaconda.id.fsset.mountFilesystems(anaconda) + + # restore the label of / to what we think it is (XXX: UUID?) + r = anaconda.id.fsset.getEntryByMountPoint("/") + r.fsystem.labelDevice(r, anaconda.rootPath) + + # for any filesystem that's _not_ on the root, we need to handle + # moving the bits from the livecd -> the real filesystems. + # this could be more clever by starting at the deepest part of + # the fsys tree, but this will do for now + for entry in anaconda.id.fsset.entries: + if entry.fsystem.isKernelFS(): + continue + + tocopy = entry.getMountPoint() + + if tocopy is None or tocopy == "/" or tocopy.startswith("/mnt") or tocopy == "swap": + continue + + log.info("doing the copy for %s" %(tocopy,)) + entry.umount(anaconda.rootPath) + entry.mount(anaconda.rootPath + "/mnt") + # XXX: should use something with selinux knowledge... + copytree("%s/%s" %(anaconda.rootPath, tocopy), + "%s/mnt/%s" %(anaconda.rootPath, tocopy)) + shutil.rmtree("%s/%s" %(anaconda.rootPath, tocopy)) + entry.umount(anaconda.rootPath + "/mnt") + entry.mount(anaconda.rootPath) + + # ensure that non-fstab filesystems are mounted in the chroot + if flags.selinux: + try: + 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) + + self._resizeRootfs(anaconda) + + wait.pop() + + def _resizeRootfs(self, anaconda): + log.info("going to do resize") + r = anaconda.id.fsset.getEntryByMountPoint("/") + rootdev = r.device.getDevice() + rc = iutil.execWithRedirect("resize2fs", + [ "/dev/%s" %(rootdev,), "-p" ], + stdout = "/dev/tty5", stderr = "/dev/tty5", + searchPath = 1) + if rc: + log.error("error running resize2fs; leaving filesystem as is") + + def doPostInstall(self, anaconda): + self._doFilesystemMangling(anaconda) + + # maybe heavy handed, but it'll do + anaconda.id.bootloader.args.append("rhgb quiet") + anaconda.id.desktop.setDefaultRunLevel(5) + + # now write out the "real" fstab and mtab + anaconda.id.fsset.write(anaconda.rootPath) + f = open(anaconda.rootPath + "/etc/mtab", "w+") + f.write(anaconda.id.fsset.mtab()) + f.close() + + # rebuild the initrd(s) + vers = self.kernelVersionList() + for (n, arch, tag) in vers: + packages.recreateInitrd(n, anaconda.rootPath) + + def writeConfiguration(self): + pass + + def kernelVersionList(self): + versions = [] + + # FIXME: we should understand more types of kernel versions and not + # be tied to rpm... + import rpm + ts = rpm.TransactionSet() + mi = ts.dbMatch('name', 'kernel') + for h in mi: + v = "%s-%s" %(h['version'], h['release']) + versions.append( (v, h['arch'], "base") ) + + return versions + + def doInitialSetup(self, anaconda): + pass + def doRepoSetup(self, anaconda): + # mount the squashfs.img to find the real os.img + iutil.mkdirChain("/mnt/installer/squashed") + isys.losetup("/dev/loop4", "%s/squashfs.img" + %(anaconda.method.getLiveCDMountPoint(),), readOnly = 1) + isys.mount("/dev/loop4", "/mnt/installer/squashed", + fstype="squashfs", readOnly = 1) + + if not os.path.exists("/mnt/installer/squashed/os.img"): + anaconda.intf.messageWindow(_("Unable to find image"), + _("The given location isn't a valid %s " + "live CD to use as an installation source.") + %(productName,), type = "custom", + custom_icon="error", + custom_buttons=[_("Exit installer")]) + + # package/group selection doesn't apply for this backend + def groupExists(self, group): + pass + def selectGroup(self, group, *args): + pass + def deselectGroup(self, group, *args): + pass + def selectPackage(self, pkg, *args): + pass + def deselectPackage(self, pkg, *args): + pass + def packageExists(self, pkg): + return True + def getDefaultGroups(self, anaconda): + return [] + def writePackagesKS(self, f): + pass diff --git a/syslogd.py b/syslogd.py index a41a29e3e..578893372 100644 --- a/syslogd.py +++ b/syslogd.py @@ -64,6 +64,9 @@ class InstSyslog: self.pid = -1; def start (self, root, log): + # don't run in the "install from livecd" case + if not os.path.exists("/usr/bin/syslogd"): + return self.pid = os.fork () if not self.pid: # look on PYTHONPATH first, so we use updated anaconda @@ -79,7 +82,8 @@ class InstSyslog: def stop(self): if self.pid == -1: - raise RuntimeError, "syslogd not running" + log.warn("syslogd not running to kill!") + return try: os.kill (self.pid, 15) except OSError, (num, msg): diff --git a/upgradeclass.py b/upgradeclass.py index cad76c41f..3d832dfe9 100644 --- a/upgradeclass.py +++ b/upgradeclass.py @@ -28,7 +28,8 @@ class InstallClass(baseclass): def requiredDisplayMode(self): return 't' - def setSteps(self, dispatch): + def setSteps(self, anaconda): + dispatch = anaconda.dispatch dispatch.setStepList( "language", "keyboard", diff --git a/yuminstall.py b/yuminstall.py index 7c030fb5f..fc5c5487d 100644 --- a/yuminstall.py +++ b/yuminstall.py @@ -743,6 +743,7 @@ class YumBackend(AnacondaBackend): def __init__ (self, method, instPath): AnacondaBackend.__init__(self, method, instPath) self.prevmedia = None + self.supportsPackageSelection = False def _handleFailure(self, url, intf): (scheme, netloc, path, query, fragment) = urlparse.urlsplit(url) |