summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJeremy Katz <katzj@redhat.com>2007-01-18 18:29:00 +0000
committerJeremy Katz <katzj@redhat.com>2007-01-18 18:29:00 +0000
commit4a7d5899d75bb5c59216af8e963534e48c932c77 (patch)
tree68d0f359fe81a4dafaeec204ac490cdfd2d46d70
parent13bb088ffbff42cda9b2b7b1acdf34f2df58a3a1 (diff)
downloadanaconda-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--ChangeLog31
-rwxr-xr-xanaconda4
-rw-r--r--backend.py14
-rw-r--r--installclass.py19
-rw-r--r--installclasses/fedora.py12
-rw-r--r--installclasses/rhel.py3
-rw-r--r--kickstart.py7
-rw-r--r--livecd.py269
-rw-r--r--syslogd.py6
-rw-r--r--upgradeclass.py3
-rw-r--r--yuminstall.py1
11 files changed, 350 insertions, 19 deletions
diff --git a/ChangeLog b/ChangeLog
index 16e71839c..476079266 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -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
diff --git a/anaconda b/anaconda
index 8c688553f..907434432 100755
--- a/anaconda
+++ b/anaconda
@@ -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)