summaryrefslogtreecommitdiffstats
path: root/image.py
diff options
context:
space:
mode:
authorChris Lumens <clumens@redhat.com>2007-12-04 20:33:36 -0500
committerChris Lumens <clumens@redhat.com>2007-12-05 10:30:46 -0500
commit2f00db0199d94709e9e84f93c54f86824d4a807f (patch)
treeae7f6ee401011e79eb7e033121942ea91d0bc34c /image.py
parent16a763e6a6f280264a8e4a034e608ce50ead8f4b (diff)
downloadanaconda-2f00db0199d94709e9e84f93c54f86824d4a807f.tar.gz
anaconda-2f00db0199d94709e9e84f93c54f86824d4a807f.tar.xz
anaconda-2f00db0199d94709e9e84f93c54f86824d4a807f.zip
Make split media image installs work.
Rework image.py into a support file with no InstallMethod subclasses in it. Move the media switching code into a yum callback and make sure our base repos have the correct attribute set to trigger using this callback. Be more careful to only attempt ejecting a CD when we're using a method that has a CD device.
Diffstat (limited to 'image.py')
-rw-r--r--image.py625
1 files changed, 207 insertions, 418 deletions
diff --git a/image.py b/image.py
index 5cfaa54dc..ebdbd53ae 100644
--- a/image.py
+++ b/image.py
@@ -1,7 +1,7 @@
#
-# image.py - Install method for disk image installs (CD & NFS)
+# Support methods for CD/DVD and ISO image installations.
#
-# Copyright 1999-2007 Red Hat, Inc.
+# Copyright 2007 Red Hat, Inc.
#
# This software may be freely redistributed under the terms of the GNU
# library public license.
@@ -10,23 +10,11 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
-
-from installmethod import InstallMethod
-import shutil
-import os
-import sys
import isys
-import time
-import stat
-import kudzu
-import string
-import shutil
-import product
-import rhpl
-import sets
-
+import os, stat, string, sys
from constants import *
+import rhpl
from rhpl.translate import _
import logging
@@ -39,6 +27,163 @@ if os.uname()[4] == "s390x":
else:
_arch = rhpl.getArch()
+def findIsoImages(path, messageWindow):
+ flush = os.stat(path)
+ files = os.listdir(path)
+ arch = _arch
+ discImages = {}
+
+ for file in files:
+ what = path + '/' + file
+ if not isys.isIsoImage(what):
+ continue
+
+ try:
+ isys.losetup("/dev/loop2", what, readOnly = 1)
+ except SystemError:
+ continue
+
+ try:
+ isys.mount("/dev/loop2", "/mnt/cdimage", fstype = "iso9660",
+ readOnly = 1)
+ for num in range(1, 10):
+ if os.access("/mnt/cdimage/.discinfo", os.R_OK):
+ f = open("/mnt/cdimage/.discinfo")
+ try:
+ f.readline() # skip timestamp
+ f.readline() # skip release description
+ discArch = string.strip(f.readline()) # read architecture
+ discNum = getDiscNums(f.readline().strip())
+ except:
+ discArch = None
+ discNum = [ 0 ]
+
+ f.close()
+
+ if num not in discNum or discArch != arch:
+ continue
+
+ # if it's disc1, it needs to have images/stage2.img
+ if (num == 1 and not
+ os.access("/mnt/cdimage/images/stage2.img", os.R_OK)):
+ log.warning("%s doesn't have a stage2.img, skipping" %(what,))
+ continue
+ # we only install binary packages, so let's look for a
+ # product/ dir and hope that this avoids getting
+ # discs from the src.rpm set
+ if not os.path.isdir("/mnt/cdimage/%s" %(productPath,)):
+ log.warning("%s doesn't have binary RPMS, skipping" %(what,))
+ continue
+
+ # warn user if images appears to be wrong size
+ if os.stat(what)[stat.ST_SIZE] % 2048:
+ rc = messageWindow(_("Warning"),
+ _("The ISO image %s has a size which is not "
+ "a multiple of 2048 bytes. This may mean "
+ "it was corrupted on transfer to this computer."
+ "\n\n"
+ "It is recommended that you exit and abort your "
+ "installation, but you can choose to continue if "
+ "you think this is in error.") % (file,),
+ type="custom", custom_icon="warning",
+ custom_buttons= [_("_Exit installer"),
+ _("_Continue")])
+ if rc == 0:
+ sys.exit(0)
+
+ discImages[num] = file
+
+ isys.umount("/mnt/cdimage", removeDir=0)
+ except SystemError:
+ pass
+
+ isys.unlosetup("/dev/loop2")
+
+ return discImages
+
+def getDiscNums(line):
+ # get the disc numbers for this disc
+ nums = line.split(",")
+ discNums = []
+ for num in nums:
+ discNums.append(int(num))
+ return discNums
+
+def getMediaId(path):
+ if os.access("%s/.discinfo" % path, os.R_OK):
+ f = open("%s/.discinfo" % path)
+ newStamp = f.readline().strip()
+ f.close()
+
+ return newStamp
+ else:
+ return None
+
+# This mounts the directory containing the iso images, and places the
+# mount point in /tmp/isodir.
+def mountDirectory(methodstr, messageWindow):
+ if methodstr.startswith("hd://"):
+ method = methodstr[5:]
+ (device, fstype, path) = method.split(":", 3)
+ device = method[0:method.index(":")]
+ else:
+ return
+
+ # First check to see if /tmp/isodir is mounted.
+ f = open("/proc/mounts", "r")
+ lines = f.readlines()
+ f.close()
+
+ for l in lines:
+ s = string.split(l)
+ if s[0] == "/dev/" + device:
+ # It is, so there's no need to try again.
+ return
+
+ try:
+ isys.mount(device, "/tmp/isodir", fstype = fstype)
+ except SystemError, msg:
+ log.error("couldn't mount ISO source directory: %s" % msg)
+ messageWindow(_("Couldn't Mount ISO Source"),
+ _("An error occurred mounting the source "
+ "device %s. This may happen if your ISO "
+ "images are located on an advanced storage "
+ "device like LVM or RAID, or if there was a "
+ "problem mounting a partition. Click exit "
+ "to abort the installation.")
+ % (self.device,), type="custom", custom_icon="error",
+ custom_buttons=[_("_Exit")])
+ sys.exit(0)
+
+def mountImage(tree, discnum, currentMedia, messageWindow, discImages={}):
+ if currentMedia:
+ raise SystemError, "trying to mount already-mounted iso image!"
+
+ if discImages == {}:
+ discImages = findIsoImages("/mnt/source", messageWindow)
+
+ while True:
+ try:
+ isoImage = "/mnt/source/%s" % (discImages[discnum])
+ isys.losetup("/dev/loop1", isoImage, readOnly = 1)
+ isys.mount("/dev/loop1", tree, fstype = 'iso9660', readOnly = 1);
+ break
+ except:
+ ans = messageWindow(_("Missing ISO 9660 Image"),
+ _("The installer has tried to mount "
+ "image #%s, but cannot find it on "
+ "the hard drive.\n\n"
+ "Please copy this image to the "
+ "drive and click Retry. Click Exit "
+ " to abort the installation.")
+ % (discnum,), type="custom",
+ custom_icon="warning",
+ custom_buttons=[_("_Exit"), _("_Retry")])
+ if ans == 0:
+ sys.exit(0)
+ elif ans == 1:
+ discImages = findIsoImages("/mnt/source", messageWindow)
+
# given groupset containing information about selected packages, use
# the disc number info in the headers to come up with message describing
# the required CDs
@@ -49,13 +194,13 @@ def presentRequiredMediaMessage(anaconda):
# if only one CD required no need to pop up a message
if len(reqcds) < 2:
- return
+ return
# check what discs our currently mounted one provides
- if os.access("/mnt/source/.discinfo", os.R_OK):
+ if os.access("/.discinfo" % anaconda.backend.ayum.tree, os.R_OK):
discNums = []
try:
- f = open("/mnt/source/.discinfo")
+ f = open("%s/.discinfo", anaconda.backend.ayum.tree)
stamp = f.readline().strip()
descr = f.readline().strip()
arch = f.readline().strip()
@@ -79,10 +224,10 @@ def presentRequiredMediaMessage(anaconda):
for cdnum in reqcds:
if cdnum == -99: # non-CD bits
continue
- reqcdstr += "\t\t%s %s disc #%d\n" % (product.productName, product.productVersion, cdnum,)
-
+ reqcdstr += "\t\t%s %s disc #%d\n" % (product.productName, product.productVersion, cdnum,)
+
return anaconda.intf.messageWindow( _("Required Install Media"),
- _("The software you have selected to "
+ _("The software you have selected to "
"install will require the following discs:\n\n"
"%s\nPlease "
"have these ready before proceeding with "
@@ -92,413 +237,57 @@ def presentRequiredMediaMessage(anaconda):
type="custom", custom_icon="warning",
custom_buttons=[_("_Reboot"), _("_Back"), _("_Continue")])
-
-
-class ImageInstallMethod(InstallMethod):
-
- def switchMedia(self, mediano, filename=""):
+def umountDirectory(self):
+ try:
+ isys.umount("/tmp/isodir", removeDir=0)
+ except:
pass
- def getMethodUri(self):
- return "file://%s" % (self.tree,)
-
- def __init__(self, tree, rootPath, intf):
- InstallMethod.__init__(self, tree, rootPath, intf)
- self.tree = tree
- self.isoPath = tree
-
-class CdromInstallMethod(ImageInstallMethod):
-
- def unmountCD(self):
- done = 0
- while done == 0:
- try:
- isys.umount("/mnt/source")
- self.currentMedia = []
- break
- except Exception, e:
- log.error("exception in unmountCD: %s" %(e,))
- self.messageWindow(_("Error"),
- _("An error occurred unmounting the disc. "
- "Please make sure you're not accessing "
- "%s from the shell on tty2 "
- "and then click OK to retry.")
- % ("/mnt/source",))
-
- def systemMounted(self, fsset, chroot):
- if not os.path.exists("%s/images/stage2.img" %(self.tree,)):
- log.debug("Not copying non-existent stage2.img")
- return
-
- self.loopbackFile = "%s%s%s" % (chroot,
- fsset.filesystemSpace(chroot)[0][0],
- "/rhinstall-stage2.img")
-
- try:
- win = self.waitWindow (_("Copying File"),
- _("Transferring install image to hard drive..."))
- shutil.copyfile("%s/images/stage2.img" % (self.tree,),
- self.loopbackFile)
- win.pop()
- except Exception, e:
- if win:
- win.pop()
-
- log.critical("error transferring stage2.img: %s" %(e,))
+def umountImage(tree, currentMedia):
+ if currentMedia is not None:
+ isys.umount(tree, removeDir=0)
+ isys.unlosetup("/dev/loop1")
- if isinstance(e, IOError) and e.errno == 5:
- msg = _("An error occurred transferring the install image "
- "to your hard drive. This is probably due to "
- "bad media.")
- else:
- msg = _("An error occurred transferring the install image "
- "to your hard drive. You are probably out of disk "
- "space.")
+def unmountCD(tree, messageWindow):
+ if not tree:
+ return
- self.messageWindow(_("Error"), msg)
- os.unlink(self.loopbackFile)
- return 1
-
- isys.lochangefd("/dev/loop0", self.loopbackFile)
-
- def switchMedia(self, mediano, filename=""):
- log.info("switching from CD %s to %s for %s" %(self.currentMedia, mediano, filename))
- if mediano in self.currentMedia:
- return
- if os.access("/mnt/source/.discinfo", os.R_OK):
- f = open("/mnt/source/.discinfo")
- timestamp = f.readline().strip()
- f.close()
- else:
- timestamp = self.timestamp
-
- if self.timestamp is None:
- self.timestamp = timestamp
-
- needed = mediano
-
- # if self.currentMedia is empty, then we shouldn't have anything
- # mounted. double-check by trying to unmount, but we don't want
- # to get into a loop of trying to unmount forever. if
- # self.currentMedia is set, then it should still be mounted and
- # we want to loop until it unmounts successfully
- if not self.currentMedia:
- try:
- isys.umount("/mnt/source")
- except:
- pass
- else:
- self.unmountCD()
-
- done = 0
-
- cdlist = []
- for (dev, something, descript) in \
- kudzu.probe(kudzu.CLASS_CDROM, kudzu.BUS_UNSPEC, 0):
- cdlist.append(dev)
-
- for dev in cdlist:
- try:
- if not isys.mount(dev, "/mnt/source", fstype = "iso9660",
- readOnly = 1):
- if os.access("/mnt/source/.discinfo", os.R_OK):
- f = open("/mnt/source/.discinfo")
- newStamp = f.readline().strip()
- try:
- descr = f.readline().strip()
- except:
- descr = None
- try:
- arch = f.readline().strip()
- except:
- arch = None
- try:
- discNum = getDiscNums(f.readline().strip())
- except:
- discNum = [ 0 ]
- f.close()
- if (newStamp == timestamp and
- arch == _arch and
- needed in discNum):
- done = 1
- self.currentMedia = discNum
-
- if not done:
- isys.umount("/mnt/source")
- except:
- pass
-
- if done:
- break
-
- if not done:
- isys.ejectCdrom(self.device)
-
- while not done:
- if self.intf is not None:
- self.intf.beep()
-
- self.messageWindow(_("Change Disc"),
- _("Please insert %s disc %d to continue.") % (productName,
- needed))
- try:
- if isys.mount(self.device, "/mnt/source",
- fstype = "iso9660", readOnly = 1):
- time.sleep(3)
- isys.mount(self.device, "/mnt/source",
- fstype = "iso9660", readOnly = 1)
-
- if os.access("/mnt/source/.discinfo", os.R_OK):
- f = open("/mnt/source/.discinfo")
- newStamp = f.readline().strip()
- try:
- descr = f.readline().strip()
- except:
- descr = None
- try:
- arch = f.readline().strip()
- except:
- arch = None
- try:
- discNum = getDiscNums(f.readline().strip())
- except:
- discNum = [ 0 ]
- f.close()
- if (newStamp == timestamp and
- arch == _arch and
- needed in discNum):
- done = 1
- self.currentMedia = discNum
- # make /tmp/cdrom again so cd gets ejected
- isys.makeDevInode(self.device, "/tmp/cdrom")
-
- if not done:
- self.messageWindow(_("Wrong Disc"),
- _("That's not the correct %s disc.")
- % (productName,))
- isys.umount("/mnt/source")
- isys.ejectCdrom(self.device)
- except:
- self.messageWindow(_("Error"),
- _("Unable to access the disc."))
-
- def filesDone(self):
+ while True:
try:
- shutil.copyfile("/mnt/source/media.repo", "%s/etc/yum.repos.d/%s-install-media.repo" %(self.rootPath, productName))
+ isys.umount(tree, removeDir=0)
+ break
except Exception, e:
- log.debug("Error copying media.repo: %s" %(e,))
-
- # we're trying to unmount the CD here. if it fails, oh well,
- # they'll reboot soon enough I guess :)
- try:
- isys.umount("/mnt/source")
- except Exception, e:
- log.error("unable to unmount source in filesDone: %s" %(e,))
-
- if not self.loopbackFile: return
-
- try:
- # this isn't the exact right place, but it's close enough
- os.unlink(self.loopbackFile)
- except SystemError:
- pass
-
- def __init__(self, method, rootPath, intf):
- """@param method cdrom://device:/path"""
- url = method[8:]
- (self.device, tree) = string.split(url, ":", 1)
- if not tree.startswith("/"):
- tree = "/%s" %(tree,)
- self.messageWindow = intf.messageWindow
- self.progressWindow = intf.progressWindow
- self.waitWindow = intf.waitWindow
- self.loopbackFile = None
-
- # figure out which disc is in. if we fail for any reason,
- # assume it's just disc1.
- if os.access("/mnt/source/.discinfo", os.R_OK):
- f = open("/mnt/source/.discinfo")
- try:
- self.timestamp = f.readline().strip()
- f.readline() # descr
- f.readline() # arch
- except:
- self.timestamp = None
-
- try:
- self.currentMedia = getDiscNums(f.readline().strip())
- except:
- self.currentMedia = [ 1 ]
-
- f.close()
- else:
- self.currentMedia = [ 1 ]
-
- ImageInstallMethod.__init__(self, tree, rootPath, intf)
-
-class NfsInstallMethod(ImageInstallMethod):
-
- def __init__(self, method, rootPath, intf):
- """@param method: nfs:/mnt/source"""
- tree = method[5:]
- ImageInstallMethod.__init__(self, tree, rootPath, intf)
- self.currentMedia = []
-
-def getDiscNums(line):
- # get the disc numbers for this disc
- nums = line.split(",")
- discNums = []
- for num in nums:
- discNums.append(int(num))
- return discNums
-
-def findIsoImages(path, messageWindow):
- flush = os.stat(path)
- files = os.listdir(path)
- arch = _arch
- discImages = {}
-
- for file in files:
- what = path + '/' + file
- if not isys.isIsoImage(what):
- continue
-
- try:
- isys.losetup("/dev/loop2", what, readOnly = 1)
- except SystemError:
- continue
+ log.error("exception in _unmountCD: %s" %(e,))
+ messageWindow(_("Error"),
+ _("An error occurred unmounting the disc. "
+ "Please make sure you're not accessing "
+ "%s from the shell on tty2 "
+ "and then click OK to retry.")
+ % (tree,))
- try:
- isys.mount("/dev/loop2", "/mnt/cdimage", fstype = "iso9660",
- readOnly = 1)
- for num in range(1, 10):
- if os.access("/mnt/cdimage/.discinfo", os.R_OK):
- f = open("/mnt/cdimage/.discinfo")
- try:
- f.readline() # skip timestamp
- f.readline() # skip release description
- discArch = string.strip(f.readline()) # read architecture
- discNum = getDiscNums(f.readline().strip())
- except:
- discArch = None
- discNum = [ 0 ]
-
- f.close()
-
- if num not in discNum or discArch != arch:
- continue
-
- # if it's disc1, it needs to have images/stage2.img
- if (num == 1 and not
- os.access("/mnt/cdimage/images/stage2.img", os.R_OK)):
- log.warning("%s doesn't have a stage2.img, skipping" %(what,))
- continue
- # we only install binary packages, so let's look for a
- # product/ dir and hope that this avoids getting
- # discs from the src.rpm set
- if not os.path.isdir("/mnt/cdimage/%s" %(productPath,)):
- log.warning("%s doesn't have binary RPMS, skipping" %(what,))
- continue
-
- # warn user if images appears to be wrong size
- if os.stat(what)[stat.ST_SIZE] % 2048:
- rc = messageWindow(_("Warning"),
- _("The ISO image %s has a size which is not "
- "a multiple of 2048 bytes. This may mean "
- "it was corrupted on transfer to this computer."
- "\n\n"
- "It is recommended that you exit and abort your "
- "installation, but you can choose to continue if "
- "you think this is in error.") % (file,),
- type="custom",
- custom_icon="warning",
- custom_buttons= [_("_Exit installer"),
- _("_Continue")])
- if rc == 0:
- sys.exit(0)
+def verifyMedia(tree, discnum, timestamp):
+ if os.access("%s/.discinfo" % tree, os.R_OK):
+ f = open("%s/.discinfo" % tree)
- discImages[num] = file
+ newStamp = f.readline().strip()
- isys.umount("/mnt/cdimage")
- except SystemError:
- pass
-
- isys.unlosetup("/dev/loop2")
-
- return discImages
+ try:
+ descr = f.readline().strip()
+ except:
+ descr = None
-class NfsIsoInstallMethod(NfsInstallMethod):
-
- def getMethodUri(self):
- return "file:///tmp/isomedia/"
-
- def switchMedia(self, mediano, filename=""):
- if mediano not in self.currentMedia:
- log.info("switching from iso %s to %s for %s" %(self.currentMedia, mediano, filename))
- self.umountImage()
- self.mountImage(mediano)
-
- def umountImage(self):
- if self.currentMedia:
- isys.umount(self.mntPoint)
- isys.unlosetup("/dev/loop3")
- self.mntPoint = None
- self.currentMedia = []
-
- def mountImage(self, cdNum):
- if (self.currentMedia):
- raise SystemError, "trying to mount already-mounted iso image!"
-
- retrymount = True
- while retrymount:
- try:
- isoImage = self.isoPath + '/' + self.discImages[cdNum]
-
- isys.losetup("/dev/loop3", isoImage, readOnly = 1)
-
- isys.mount("/dev/loop3", "/tmp/isomedia", fstype = 'iso9660', readOnly = 1);
- self.mntPoint = "/tmp/isomedia/"
- self.currentMedia = [ cdNum ]
-
- retrymount = False
- except:
- ans = self.messageWindow( _("Missing ISO 9660 Image"),
- _("The installer has tried to mount "
- "image #%s, but cannot find it on "
- "the server.\n\n"
- "Please copy this image to the "
- "remote server's share path and "
- "click Retry. Click Exit to "
- "abort the installation.")
- % (cdNum,), type="custom",
- custom_icon="warning",
- custom_buttons=[_("_Exit"),
- _("_Retry")])
- if ans == 0:
- sys.exit(0)
- elif ans == 1:
- self.discImages = findIsoImages(self.isoPath, self.messageWindow)
-
- def filesDone(self):
- # if we can't unmount the cd image, we really don't care much
- # let them go along and don't complain
try:
- self.umountImage()
- except Exception, e:
- log.error("unable to unmount image in filesDone: %s" %(e,))
- pass
+ arch = f.readline().strip()
+ except:
+ arch = None
- def __init__(self, method, rootPath, intf):
- """@param method: nfsiso:/mnt/source"""
- tree = method[8:]
- ImageInstallMethod.__init__(self, "/%s" % tree, rootPath, intf)
- self.messageWindow = intf.messageWindow
+ try:
+ discs = getDiscNums(f.readline().strip())
+ except:
+ discs = [ 0 ]
- # the tree points to the directory that holds the iso images
- # even though we already have the main one mounted once, it's
- # easiest to just mount it again so that we can treat all of the
- # images the same way -- we use loop3 for everything
- self.currentMedia = []
+ f.close()
+ if (newStamp == timestamp and arch == _arch and discnum in discs):
+ return True
- self.discImages = findIsoImages(tree, self.messageWindow)
- self.mountImage(1)
+ return False