[Fedora-livecd-list] imgcreate/creator.py imgcreate/kickstart.py tools/edit-livecd

Brian C. Lane bcl at fedoraproject.org
Thu Jan 12 00:10:38 UTC 2012


 imgcreate/creator.py   |    4 -
 imgcreate/kickstart.py |    8 ++
 tools/edit-livecd      |  151 +++++++++++++++++++++++++++++++++++++++++++++----
 3 files changed, 150 insertions(+), 13 deletions(-)

New commits:
commit 37e07b2fa8b347b37f5ff46fb36e32cc068fb29d
Author: Alan Pevec <apevec at redhat.com>
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 deb9d95..745a481 100644
--- a/imgcreate/creator.py
+++ b/imgcreate/creator.py
@@ -658,7 +658,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")
@@ -717,7 +717,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 09f5ed3..9fdd683 100644
--- a/imgcreate/kickstart.py
+++ b/imgcreate/kickstart.py
@@ -546,6 +546,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:




More information about the livecd mailing list