imgcreate/creator.py | 4 - imgcreate/kickstart.py | 8 ++ tools/edit-livecd | 151 +++++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 150 insertions(+), 13 deletions(-)
New commits: commit d2271618689b87a1e92868c330d7251aa693cbc7 Author: Alan Pevec <[email protected]> Date: Thu Dec 22 00:55:37 2011 +0100 edit-livecd: -k --kickstart option Adds kickstart option for using kickstart file as an recipe for editing a livecd image. Following directives are honored: part / --size <new rootfs size to be resized to> bootloader --append "!opt-to-remove opt-to-add" repo --baseurl=file://path-to-RPMs %pre %post %packages Note that repo only supports a local directory of RPMs, not a yum repo. diff --git a/imgcreate/creator.py b/imgcreate/creator.py index 675dcf6..dc47b86 100644 --- a/imgcreate/creator.py +++ b/imgcreate/creator.py @@ -683,7 +683,7 @@ class ImageCreator(object): except: pass - def __run_post_scripts(self): + def _run_post_scripts(self): for s in kickstart.get_post_scripts(self.ks): (fd, path) = tempfile.mkstemp(prefix = "ks-script-", dir = self._instroot + "/tmp") @@ -742,7 +742,7 @@ class ImageCreator(object): self._create_bootconfig() - self.__run_post_scripts() + self._run_post_scripts() kickstart.SelinuxConfig(self._instroot).apply(ksh.selinux) def launch_shell(self): diff --git a/imgcreate/kickstart.py b/imgcreate/kickstart.py index 33859d6..140aba0 100644 --- a/imgcreate/kickstart.py +++ b/imgcreate/kickstart.py @@ -545,6 +545,14 @@ def inst_langs(ks): return ks.handler.packages.instLangs return "" +def get_pre_scripts(ks): + scripts = [] + for s in ks.handler.scripts: + if s.type != ksparser.KS_SCRIPT_PRE: + continue + scripts.append(s) + return scripts + def get_post_scripts(ks): scripts = [] for s in ks.handler.scripts: diff --git a/tools/edit-livecd b/tools/edit-livecd index 659cfae..ab86960 100755 --- a/tools/edit-livecd +++ b/tools/edit-livecd @@ -29,12 +29,16 @@ import shutil import subprocess import optparse import logging +import rpm +import glob from imgcreate.debug import * from imgcreate.errors import * from imgcreate.fs import * from imgcreate.live import * from imgcreate.creator import * +import imgcreate.kickstart as kickstart +from imgcreate import read_kickstart class ExistingSparseLoopbackDisk(SparseLoopbackDisk): """don't want to expand the disk""" @@ -110,6 +114,9 @@ class LiveImageEditor(LiveImageCreator): self._LiveImageCreatorBase__isodir = None """directory where the iso is staged""" + self.ks = None + """optional kickstart file as a recipe for editing the image""" + # properties def __get_image(self): if self._LoopImageCreator__imagedir is None: @@ -206,7 +213,8 @@ class LiveImageEditor(LiveImageCreator): self._get_fslabel() self.fslabel = self._LoopImageCreator__fslabel - self._LoopImageCreator__image_size = os.stat(self._image)[stat.ST_SIZE] + if self._LoopImageCreator__image_size == None: + self._LoopImageCreator__image_size = os.stat(self._image)[stat.ST_SIZE] self._LoopImageCreator__instloop = ExtDiskMount( ExistingSparseLoopbackDisk(self._image, @@ -355,12 +363,15 @@ class LiveImageEditor(LiveImageCreator): except IOError, e: raise CreatorError("Failed to open '%s' : %s" % (fpath, e)) else: + release = None for line in cfgf: i = line.find('Welcome to ') if i > -1: release = line[i+11:-2] break cfgf.close() + if not release: + return ntext = dt.translate(None, '-') + '-' + _builder + '-Remix-' + release @@ -396,12 +407,33 @@ class LiveImageEditor(LiveImageCreator): if os.path.exists(src): os.rename(src, cfgf) - args = ['/bin/sed', '-i', - '-e', 's/Welcome to .*/Welcome to ' + self._releasefile + '!/', - '-e', 's/root=[^ ]*/root=live:CDLABEL=' + self.name + '/', - '-e', 's/rootfstype=[^ ]* [^ ]*/rootfstype=auto ro/', - '-e', 's/liveimg .* quiet/liveimg quiet/', cfgf] - + args = ['/bin/sed', '-i'] + if self._releasefile: + args.append('-e') + args.append('s/Welcome to .*/Welcome to ' + self._releasefile + '!/') + if self.clone: + args.append('-e') + args.append('s/rootfstype=[^ ]* [^ ]*/rootfstype=auto ro/') + args.append('-e') + args.append('s/liveimg .* quiet/liveimg quiet/') + args.append('-e') + args.append('s/root=[^ ]*/root=live:CDLABEL=' + self.name + '/') + if self.ks: + # bootloader --append "!opt-to-remove opt-to-add" + for param in kickstart.get_kernel_args(self.ks,"").split(): + if param.startswith('!'): + param=param[1:] + # remove parameter prefixed with ! + args.append('-e') + args.append("/^ append/s/%s //" % param) + # special case for last parameter + args.append('-e') + args.append("/^ append/s/%s$//" % param) + else: + # append parameter + args.append('-e') + args.append("/^ append/s/$/ %s/" % param) + args.append(cfgf) dev_null = os.open("/dev/null", os.O_WRONLY) try: subprocess.Popen(args, @@ -415,10 +447,96 @@ class LiveImageEditor(LiveImageCreator): finally: os.close(dev_null) + def _run_pre_scripts(self): + for s in kickstart.get_pre_scripts(self.ks): + (fd, path) = tempfile.mkstemp(prefix = "ks-script-", + dir = self._instroot + "/tmp") + + os.write(fd, s.script) + os.close(fd) + os.chmod(path, 0700) + + env = self._get_post_scripts_env(s.inChroot) + + if not s.inChroot: + env["INSTALL_ROOT"] = self._instroot + preexec = None + script = path + else: + preexec = self._chroot + script = "/tmp/" + os.path.basename(path) + + try: + subprocess.check_call([s.interp, script], + preexec_fn = preexec, env = env) + except OSError, e: + raise CreatorError("Failed to execute %%post script " + "with '%s' : %s" % (s.interp, e.strerror)) + except subprocess.CalledProcessError, err: + if s.errorOnFail: + raise CreatorError("%%post script failed with code %d " + % err.returncode) + logging.warning("ignoring %%post failure (code %d)" + % err.returncode) + finally: + os.unlink(path) + + class simpleCallback: + def __init__(self): + self.fdnos = {} + + def callback(self, what, amount, total, mydata, wibble): + if what == rpm.RPMCALLBACK_TRANS_START: + pass + + elif what == rpm.RPMCALLBACK_INST_OPEN_FILE: + hdr, path = mydata + print "Installing %s\r" % (hdr["name"]) + fd = os.open(path, os.O_RDONLY) + nvr = '%s-%s-%s' % ( hdr['name'], hdr['version'], hdr['release'] ) + self.fdnos[nvr] = fd + return fd + + elif what == rpm.RPMCALLBACK_INST_CLOSE_FILE: + hdr, path = mydata + nvr = '%s-%s-%s' % ( hdr['name'], hdr['version'], hdr['release'] ) + os.close(self.fdnos[nvr]) + + elif what == rpm.RPMCALLBACK_INST_PROGRESS: + hdr, path = mydata + print "%s: %.5s%% done\r" % (hdr["name"], (float(amount) / total) * 100), + + def install_rpms(self): + if kickstart.exclude_docs(self.ks): + rpm.addMacro("_excludedocs", "1") + if not kickstart.selinux_enabled(self.ks): + rpm.addMacro("__file_context_path", "%{nil}") + if kickstart.inst_langs(self.ks) != None: + rpm.addMacro("_install_langs", kickstart.inst_langs(self.ks)) + # start RPM transaction + ts=rpm.TransactionSet(self._instroot) + for repo in kickstart.get_repos(self.ks): + (name, baseurl, mirrorlist, proxy, inc, exc) = repo + if baseurl.startswith("file://"): + baseurl=baseurl[7:] + elif not baseurl.startswith("/"): + raise CreatorError("edit-livecd accepts only --baseurl pointing to a local folder with RPMs (not YUM repo)") + if not baseurl.endswith("/"): + baseurl+="/" + for pkg_from_list in kickstart.get_packages(self.ks): + # TODO report if package listed in ks is missing + for pkg in glob.glob(baseurl+pkg_from_list+"-[0-9]*.rpm"): + fdno = os.open(pkg, os.O_RDONLY) + hdr = ts.hdrFromFdno(fdno) + os.close(fdno) + ts.addInstall(hdr,(hdr,pkg), "u") + ts.run(self.simpleCallback().callback,'') + def parse_options(args): parser = optparse.OptionParser(usage = """ %prog [-n=<name>] [-o=<output>] + [-k=<kickstart-file>] [-s=<script.sh>] [-t=<tmpdir>] [-e=<excludes>] @@ -439,6 +557,9 @@ def parse_options(args): parser.add_option("-o", "--output", type="string", dest="output", help="specify directory for new iso file.") + parser.add_option("-k", "--kickstart", type="string", dest="kscfg", + help="Path or url to kickstart config file") + parser.add_option("-s", "--script", type="string", dest="script", help="specify script to run chrooted in the LiveOS " "fsimage") @@ -530,10 +651,10 @@ def main(): print >> sys.stderr, "You must run edit-liveos as root" return 1 - if stat.S_ISBLK(os.stat(LiveOS).st_mode): - name = get_fsvalue(LiveOS, 'LABEL') + '.edited' - elif options.name and options.name != os.path.basename(LiveOS): + if options.name: name = options.name + elif stat.S_ISBLK(os.stat(LiveOS).st_mode): + name = get_fsvalue(LiveOS, 'LABEL') + '.edited' else: name = os.path.basename(LiveOS) + ".edited" @@ -557,9 +678,17 @@ def main(): editor.skip_minimize = options.skip_minimize try: + if options.kscfg: + editor.ks = read_kickstart(options.kscfg) + # part / --size <new rootfs size to be resized to> + editor._LoopImageCreator__image_size = kickstart.get_image_size(editor.ks) editor.mount(LiveOS, cachedir = None) editor._configure_bootloader(editor._LiveImageCreatorBase__isodir) - if options.script: + if editor.ks: + editor._run_pre_scripts() + editor.install_rpms() + editor._run_post_scripts() + elif options.script: print "Running edit script '%s'" % options.script editor._run_script(options.script) else: -- livecd mailing list [email protected] https://admin.fedoraproject.org/mailman/listinfo/livecd
