[patch 08/20] No params to createIso()
by Mark McLoughlin
Within createIso() use self.fs_label rather than passing
filename to it (since e.g. it's not just used as a filename)
Signed-off-by: Mark McLoughlin <markmc(a)redhat.com>
Index: livecd/creator/livecd-creator
===================================================================
--- livecd.orig/creator/livecd-creator
+++ livecd/creator/livecd-creator
@@ -684,14 +684,14 @@ label runfromram
self.createInitramfs()
self.configureBootloader()
- def createIso(self, filename):
+ def createIso(self):
"""write out the live CD ISO"""
- subprocess.call(["/usr/bin/mkisofs", "-o", "%s.iso" %(filename,),
+ subprocess.call(["/usr/bin/mkisofs", "-o", "%s.iso" %(self.fs_label,),
"-b", "isolinux/isolinux.bin",
"-c", "isolinux/boot.cat",
"-no-emul-boot", "-boot-load-size", "4",
"-boot-info-table", "-J", "-r", "-hide-rr-moved",
- "-V", "%s" %(filename,), "%s/out" %(self.build_dir)])
+ "-V", "%s" %(self.fs_label,), "%s/out" %(self.build_dir)])
def createSquashFS(self):
"""create compressed squashfs file system"""
@@ -707,7 +707,7 @@ label runfromram
def package(self):
self.createSquashFS()
- self.createIso(self.fs_label)
+ self.createIso()
def usage():
print """
--
17 years
[patch 07/20] Fold code from main() into InstallationTarget
by Mark McLoughlin
There's a lot of code in main() at the moment that looks to
me like it would be better in InstallationTarget()
To that end:
- Make the InstallationTarget constructor take the majority
of command-line options
- Add a parse() method which parses the kickstart file, and
adds packages/repos/etc.
- Consolidate all the install stuff into a new install()
method
- Consolidate the "package the system image into an ISO" stuff
into a package() method
Signed-off-by: Mark McLoughlin <markmc(a)redhat.com>
Index: livecd/creator/livecd-creator
===================================================================
--- livecd.orig/creator/livecd-creator
+++ livecd/creator/livecd-creator
@@ -268,9 +268,41 @@ def get_kernel_version(root):
return kver
class InstallationTarget:
- def __init__(self):
+ def __init__(self, repos, packages, epackages, groups, fs_label, skip_compression):
self.ayum = LiveCDYum()
+ self.repos = repos
+ self.packages = packages
+ self.epackages = epackages
+ self.groups = groups
+ self.fs_label = fs_label
+ self.skip_compression = skip_compression
+
+ self.build_dir = None
+ self.instloop = None
+ self.bindmounts = []
+ self.ksparser = None
+ def parse(self, kscfg):
+ ksversion = pykickstart.version.makeVersion()
+ self.ksparser = pykickstart.parser.KickstartParser(ksversion)
+ if kscfg:
+ self.ksparser.readKickstart(kscfg)
+
+ for repo in self.ksparser.handler.repo.repoList:
+ already_given = False
+ for cmd_name, cmd_url in self.repos:
+ if cmd_name == repo.name:
+ already_given = True
+ break
+
+ if not already_given:
+ self.repos.append( (repo.name, repo.baseurl) )
+
+ self.packages.extend(self.ksparser.handler.packages.packageList)
+ self.groups.extend(map(lambda g: (g.name, g.include),
+ self.ksparser.handler.packages.groupList))
+ self.epackages.extend(self.ksparser.handler.packages.excludedList)
+
def base_on_iso(self, base_on):
"""helper function to extract ext3 file system from a live CD ISO"""
@@ -299,14 +331,9 @@ class InstallationTarget:
return success
- def setup(self, image_size, fs_label, base_on):
+ def setup(self, image_size, base_on = None):
"""setup target ext3 file system in preparation for an install"""
- # global variables needed
- self.fs_label = fs_label
- self.instloop = None
- self.bindmounts = []
-
# setup temporary build dirs
try:
self.build_dir = tempfile.mkdtemp(dir="/var/tmp", prefix="livecd-creator-")
@@ -569,11 +596,11 @@ class InstallationTarget:
def relabelSystem(self):
# finally relabel all files
- instroot = "%s/install_root" %(self.build_dir,)
- if os.path.exists("%s/sbin/restorecon" %(instroot,)):
- subprocess.call(["/sbin/restorecon", "-v", "-r", "/"],
- preexec_fn=self.run_in_root)
- return True
+ if self.ksparser.handler.selinux.selinux:
+ instroot = "%s/install_root" %(self.build_dir,)
+ if os.path.exists("%s/sbin/restorecon" %(instroot,)):
+ subprocess.call(["/sbin/restorecon", "-v", "-r", "/"],
+ preexec_fn=self.run_in_root)
def launchShell(self):
print "Launching shell. Exit to continue."
@@ -644,13 +671,21 @@ label runfromram
# TODO: enable external entitity to partipate in adding boot entries
+ def install(self):
+ for (name, url) in self.repos:
+ self.ayum.addRepository(name, url)
+
+ if not self.installPackages(self.packages, self.epackages, self.groups):
+ self.teardown()
+ sys.exit(1)
+
+ self.configureSystem(self.ksparser)
+ self.relabelSystem()
+ self.createInitramfs()
+ self.configureBootloader()
+
def createIso(self, filename):
"""write out the live CD ISO"""
-
- # ship the ext3 image if there is no compressed image (happens during --skip-compression)
- if not os.path.exists("%s/out/squashfs.img" %(self.build_dir,)):
- shutil.move("%s/data/os.img" %(self.build_dir,), "%s/out/ext3fs.img" %(self.build_dir,))
-
subprocess.call(["/usr/bin/mkisofs", "-o", "%s.iso" %(filename,),
"-b", "isolinux/isolinux.bin",
"-c", "isolinux/boot.cat",
@@ -660,11 +695,19 @@ label runfromram
def createSquashFS(self):
"""create compressed squashfs file system"""
- # FIXME: mksquashfs segfaults if PWD isn't set in the environment
- subprocess.call(["/sbin/mksquashfs", "os.img", "sysroot",
- "../out/squashfs.img"],
- cwd="%s/data" %(self.build_dir,),
- env={"PWD": "%s/data" %(self.build_dir,)})
+ if not self.skip_compression:
+ # FIXME: mksquashfs segfaults if PWD isn't set in the environment
+ subprocess.call(["/sbin/mksquashfs", "os.img", "sysroot",
+ "../out/squashfs.img"],
+ cwd="%s/data" %(self.build_dir,),
+ env={"PWD": "%s/data" %(self.build_dir,)})
+ else:
+ shutil.move("%s/data/os.img" %(self.build_dir,),
+ "%s/out/ext3fs.img" %(self.build_dir,))
+
+ def package(self):
+ self.createSquashFS()
+ self.createIso(self.fs_label)
def usage():
print """
@@ -770,56 +813,28 @@ def main():
print "Unknown option %s"%o
sys.exit(2)
- ksversion = pykickstart.version.makeVersion()
- ksparser = pykickstart.parser.KickstartParser(ksversion)
- if not kscfg:
- if len(packages) == 0:
- print "No packages specified."
- print ""
- usage()
- sys.exit(1)
+ if not kscfg and not (packages or groups):
+ print "No packages or groups specified."
+ print ""
+ usage()
+ sys.exit(1)
- if len(repos) == 0:
- print "No repositories specified."
- print ""
- usage()
- sys.exit(1)
- else:
- ksparser.readKickstart(kscfg)
+ if not kscfg and not repos:
+ print "No repositories specified."
+ print ""
+ usage()
+ sys.exit(1)
- for repo in ksparser.handler.repo.repoList:
- given_on_cmd_line = False
- for cmd_name, cmd_url in repos:
- if cmd_name == repo.name:
- given_on_cmd_line = True
- break
-
- if not given_on_cmd_line:
- repos.append( (repo.name, repo.baseurl) )
-
- packages.extend(ksparser.handler.packages.packageList)
- groups.extend(map(lambda g: (g.name, g.include),
- ksparser.handler.packages.groupList))
- epackages.extend(ksparser.handler.packages.excludedList)
+ target = InstallationTarget(repos, packages, epackages, groups, fs_label, skip_compression)
- target = InstallationTarget()
+ target.parse(kscfg)
- if not target.setup(uncompressed_size, fs_label, base_on):
+ if not target.setup(uncompressed_size, base_on):
print "Cannot setup installation target. Aborting."
sys.exit(1)
try:
- for (n, u) in repos:
- target.addRepository(n, u)
-
- if not target.installPackages(packages, epackages, groups):
- target.teardown()
- sys.exit(1)
- target.configureSystem(ksparser)
- target.createInitramfs()
- if ksparser.handler.selinux.selinux:
- target.relabelSystem()
- target.configureBootloader()
+ target.install()
except SystemExit:
sys.exit(1)
except:
@@ -833,9 +848,7 @@ def main():
target.launchShell()
target.unmount()
- if not skip_compression:
- target.createSquashFS()
- target.createIso(fs_label)
+ target.package()
target.teardown()
if __name__ == "__main__":
--
17 years
[patch 06/20] Cleanup unmount() and teardown()
by Mark McLoughlin
Misc cleanups:
- unmount the bindmounts and remove yum-cache even if
instloop isn't defined
- consolidate the build dir deletion into a single rmtree()
- only attempt to unmount() if the build dir has been
created
Signed-off-by: Mark McLoughlin <markmc(a)redhat.com>
Index: livecd/creator/livecd-creator
===================================================================
--- livecd.orig/creator/livecd-creator
+++ livecd/creator/livecd-creator
@@ -403,41 +403,25 @@ class InstallationTarget:
def unmount(self):
"""detaches system bind mounts and install_root for the file system and tears down loop devices used"""
+ shutil.rmtree(self.build_dir + "/yum-cache", ignore_errors=True)
try:
os.unlink(self.build_dir + "/install_root/etc/mtab")
except OSError:
pass
- # TODO: need to handle when unmount fails because of lingering
- # processes that has open files
+ self.bindmounts.reverse()
+ for b in self.bindmounts:
+ b.umount()
+
if self.instloop:
- self.bindmounts.reverse()
- for b in self.bindmounts:
- b.umount()
self.instloop.cleanup()
- try:
- os.unlink(self.build_dir + "/yum.conf")
- except OSError:
- pass
- shutil.rmtree("%s/yum-cache" %(self.build_dir,), ignore_errors=True)
-
self.instloop = None
def teardown(self):
- """releases all resources and removes all temporary files"""
-
- # ensure we've detached the install_root
- self.unmount()
-
- # this removes all data used in the temporary build directory
- try:
- os.unlink(self.build_dir + "/data/os.img")
- os.rmdir(self.build_dir + "/data")
- except OSError:
- pass
- shutil.rmtree("%s/out" %(self.build_dir,), ignore_errors=True)
- shutil.rmtree(self.build_dir, ignore_errors=True)
+ if self.build_dir:
+ self.unmount()
+ shutil.rmtree(self.build_dir, ignore_errors = True)
def addRepository(self, name, url):
"""adds a yum repository to temporary yum.conf file used"""
--
17 years
[patch 05/20] Dont import .git .pc AUTHORS COPYING HACKING Makefile README TODO config creator installer livecd-tools.spec patches from pykickstart
by Mark McLoughlin
Using import * with the pykickstart makes it a little difficult
in places to figure out where stuff is coming from - e.g. if
it was KS_GROUP_DEFAULT instead of GROUP_DEFAULT, it wouldn't
be so bad, but ...
Let's just use the fully qualified names of symbols.
Signed-off-by: Mark McLoughlin <markmc(a)redhat.com>
Index: livecd/creator/livecd-creator
===================================================================
--- livecd.orig/creator/livecd-creator
+++ livecd/creator/livecd-creator
@@ -24,11 +24,9 @@ import time
import traceback
import subprocess
import shutil
-
-from pykickstart.parser import *
-from pykickstart.version import *
-
import yum
+import pykickstart.parser
+import pykickstart.version
class BindChrootMount:
"""Represents a bind mount of a directory into a chroot."""
@@ -214,11 +212,11 @@ class LiveCDYum(yum.YumBase):
else:
print >> sys.stderr, "No such package %s to remove" %(pkg,)
- def selectGroup(self, grp, include = GROUP_DEFAULT):
+ def selectGroup(self, grp, include = pykickstart.parser.GROUP_DEFAULT):
yum.YumBase.selectGroup(self, grp)
- if include == GROUP_REQUIRED:
+ if include == pykickstart.parser.GROUP_REQUIRED:
map(lambda p: self.deselectPackage(p), grp.default_packages.keys())
- elif include == GROUP_ALL:
+ elif include == pykickstart.parser.GROUP_ALL:
map(lambda p: self.selectPackage(p), grp.optional_packages.keys())
def addRepository(self, name, url = None, mirrorlist = None):
@@ -549,7 +547,7 @@ class InstallationTarget:
f.close()
# and now, for arbitrary %post scripts
- for s in filter(lambda s: s.type == KS_SCRIPT_POST,
+ for s in filter(lambda s: s.type == pykickstart.parser.KS_SCRIPT_POST,
ksparser.handler.scripts):
# we can only safely run scripts in the chroot
if not s.inChroot:
@@ -769,7 +767,7 @@ def main():
continue
if o in ("-p", "--package"):
if a.startswith("@"):
- groups.append((a[1:], GROUP_DEFAULT))
+ groups.append((a[1:], pykickstart.parser.GROUP_DEFAULT))
else:
packages.append(a)
continue
@@ -788,7 +786,8 @@ def main():
print "Unknown option %s"%o
sys.exit(2)
- ksparser = KickstartParser(makeVersion())
+ ksversion = pykickstart.version.makeVersion()
+ ksparser = pykickstart.parser.KickstartParser(ksversion)
if not kscfg:
if len(packages) == 0:
print "No packages specified."
--
17 years
[patch 04/20] Another use of map() in installPackages()
by Mark McLoughlin
I'm not a big fan of map(), but it seems strange to use it on
packages and exclude-packages, but not groups in installPackages()
Signed-off-by: Mark McLoughlin <markmc(a)redhat.com>
Index: livecd/creator/livecd-creator
===================================================================
--- livecd.orig/creator/livecd-creator
+++ livecd/creator/livecd-creator
@@ -451,8 +451,7 @@ class InstallationTarget:
def installPackages(self, packageList, excludePackageList, groupList = []):
"""install packages into target file system"""
map(lambda pkg: self.ayum.selectPackage(pkg), packageList)
- for (grp, inc) in groupList:
- self.ayum.selectGroup(grp, inc)
+ map(lambda grp: self.ayum.selectGroup(grp[0], grp[1]), groupList)
map(lambda pkg: self.ayum.deselectPackage(pkg), excludePackageList)
try:
--
17 years
[patch 03/20] Cleanup failure handling in base_on_iso()
by Mark McLoughlin
InstallationTarget.base_on_iso() has four places where it
calls cleanup() on the LoopbackMounts. Consolidate those
into one using try/finally.
Signed-off-by: Mark McLoughlin <markmc(a)redhat.com>
Index: livecd/creator/livecd-creator
===================================================================
--- livecd.orig/creator/livecd-creator
+++ livecd/creator/livecd-creator
@@ -277,33 +277,28 @@ class InstallationTarget:
"""helper function to extract ext3 file system from a live CD ISO"""
isoloop = LoopbackMount(base_on, "%s/base_on_iso" %(self.build_dir,))
- if not isoloop.setup():
- isoloop.cleanup()
- return False
squashloop = LoopbackMount("%s/squashfs.img" %(isoloop.mountdir,),
"%s/base_on_squashfs" %(self.build_dir,),
"squashfs")
- if not squashloop.setup():
- squashloop.cleanup()
- isoloop.cleanup()
- return False
- # copy the ext3 fs out
+ success = False
try:
- shutil.copyfile("%s/base_on_squashfs/os.img" %(self.build_dir,),
- "%s/data/os.img" %(self.build_dir,))
- except Exception, e:
- print "Cannot copy os.img from squashfs from ISO to base on"
- traceback.print_exc(file=sys.stderr)
+ if isoloop.setup() and squashloop.setup():
+ # copy the ext3 fs out
+ try:
+ shutil.copyfile("%s/base_on_squashfs/os.img" %(self.build_dir,),
+ "%s/data/os.img" %(self.build_dir,))
+ success = True
+ except Exception, e:
+ print "Cannot copy os.img from squashfs from ISO to base on"
+ traceback.print_exc(file=sys.stderr)
+ finally:
+ # unmount and tear down the mount points and loop devices used
squashloop.cleanup()
isoloop.cleanup()
- return False
- # unmount and tear down the mount points and loop devices used
- squashloop.cleanup()
- isoloop.cleanup()
- return True
+ return success
def setup(self, image_size, fs_label, base_on):
--
17 years
[patch 02/20] Use mkdtemp() rather than mktemp()
by Mark McLoughlin
We currently create the build directory by using mktemp() to find
a unqiue filename in /tmp and then use that filename as the name
of the directory to create in /var/tmp. Just weird, frankly.
That's just weird, frankly. We should use mkdtemp() instead.
Also, unconditionally delete the build dir.
Signed-off-by: Mark McLoughlin <markmc(a)redhat.com>
Index: livecd/creator/livecd-creator
===================================================================
--- livecd.orig/creator/livecd-creator
+++ livecd/creator/livecd-creator
@@ -315,11 +315,10 @@ class InstallationTarget:
self.bindmounts = []
# setup temporary build dirs
- self.build_dir = "/var/tmp/livecd-creator/build-" + os.path.basename(tempfile.mktemp())
try:
- os.makedirs(self.build_dir)
- except OSError:
- print "Cannot create build directory at %s" %(self.build_dir,)
+ self.build_dir = tempfile.mkdtemp(dir="/var/tmp", prefix="livecd-creator-")
+ except OSError, e:
+ print "Cannot create build directory in /var/tmp: %s" % e
return False
try:
@@ -445,8 +444,7 @@ class InstallationTarget:
except OSError:
pass
shutil.rmtree("%s/out" %(self.build_dir,), ignore_errors=True)
- if "/var/tmp/livecd-creator/build-" in self.build_dir:
- shutil.rmtree(self.build_dir, ignore_errors=True)
+ shutil.rmtree(self.build_dir, ignore_errors=True)
def addRepository(self, name, url):
"""adds a yum repository to temporary yum.conf file used"""
--
17 years
[patch 01/20] Fix usage string
by Mark McLoughlin
livecd-creator requires you to run with at least --config or --package/--repo
Make an attempt to convey that in the usage string
Signed-off-by: Mark McLoughlin <markmc(a)redhat.com>
Index: livecd/creator/livecd-creator
===================================================================
--- livecd.orig/creator/livecd-creator
+++ livecd/creator/livecd-creator
@@ -695,10 +695,10 @@ label runfromram
def usage():
print """
usage: livecd-creator [--help]
- [--config=<path-to-kickstart-file>]
- [--repo=<name1>,<url1> ...] [--repo=<name2>,<url2>]
- --package=<p1> [--package=<p2> ...]
- [--exclude-package=<e1>] --exclude-package=<e2> ...]
+ [--config=<path-to-kickstart-file> | --repo=<name>,<url> --package=<p>]
+ [--repo=<name1>,<url1>] [--repo=<name2>,<url2> ...]
+ [--package=<p1>] [--package=<p2> ...]
+ [--exclude-package=<e1>] [--exclude-package=<e2> ...]
[--base-on=<path-to-iso-file>]
[--fslabel=<label>]
[--skip-compression]
--
17 years
[patch 00/20] Fix error handling and misc. cleanups
by Mark McLoughlin
Hi,
Apologies for spamming the list with 20 mails, but hopefully
it'll make it easier to review the patches.
The background to this is that I was suprised to see that
livecd-creator had rather poor error handling (e.g. not unmounting
stuff after certain errors, obscure error messages etc.) since
I thought that was the main reason to re-write pilgrim in python :-)
It's understandable, though. Because Python has exceptions,
it's easy to be fooled into thinking you have error conditions
under control, but in reality, exceptions probably makes it easier
to screw up error handling.
So, here's an attempt to fix it based on the following
guidelines:
- don't intercept an exception if you can't do something useful
with it. Corollary: intercept an exception if you can do something
useful with it.
- anticipate user errors or user-fixable system errors in a user
friendly manner. Corollary: allow other types of exceptions
to be passed to the toplevel.
- i.e. there are 3 types of errors/exceptions, and 3 different
strategies for handling them:
1) errors which we can handle ourselves without ever telling
the user - e.g. a directory which we wish to create already
exists
2) user errors, or predictable system errors, which we
should do our best to help the user out with - e.g.
a busted kickstart file or running out of loop devices
3) pathological system errors or programmer errors, all
of which can be just allowed to propogate up to the
toplevel where an ugly traceback will be printed -
e.g creating a dir under the temporary build dir
fails or an unmount fails because we didn't close
the rpmdb
- whatever the error, we should do our best to clean up files,
directories, mounts, etc. which we created
- bear in mind that e.g. Ctrl-C can happen at any time
Also, I've cleanup up various parts of the code as I
worked on the error handling. Those patches come first.
Cheers,
Mark.
--
17 years
RE: Patches on Error Case, Error Message Print Out
by john pleines
Hi Folks,
I've been following the threads on the list regarding the errors and loop
filesystems being left behind. I'm still running across
similar problems, and thought I'd through my data into the mix, in the hope
it may help point to the problem.
Configuration Particulars:
I'm running FC6 from the January 2007 re-spins, with patches as of this
morning (03/07).
I have a single local repository from the Januaury 2007 re-spin CDs.
With the version of livecd tools out of git with Jeremy's latest
livecd-fix-unmount-on-error.patch and
livecd-better-errors.patch applied.
Still see filesystems left behind.
I have attached the kickstart file I'm using, and the output from
livecd-creator.
Thanks folks for all your efforts on this.
John
17 years