diff options
Diffstat (limited to 'livecd.py')
-rw-r--r-- | livecd.py | 269 |
1 files changed, 269 insertions, 0 deletions
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 |