[Fedora-livecd-list] [PATCH] add edit-livecd script for editing livecd based images
David Huff
dhuff at redhat.com
Thu Mar 11 14:49:07 UTC 2010
Is there any objections to including this script in livecd-toosls? We
have seen a good amount of interest in something like this.
-D
On 03/09/2010 04:25 PM, Joey Boggs wrote:
> Resubmitting the edit-livecd script with the latest updates.
>
> edit-livecd is used for opening up livecd images to add and/or edit files with out having to rebuild the iso image.
>
> This script is useful for people who don't have the development and yum infrastructure/repos set up to build livecd's from scratch. This is especially useful for custom livecd images, like the ovirt-node project[1].
>
> We agree that changes to packaging and binaries should always be done by modifying kickstart and rebuilding the image via livecd-creator. But if all you want to do is add/edit a config file, public key for SSH, or change the root password this can be done with the edit-livecd script.
>
> This is the second generation of the edit-livecd tool that replaces our original bash script with a python version and aims to reuse as much as the livecd-creator libraries as possible. The main reason for the new creator class is to avoid the need for a kisckstart file in order to edit an existing livecd image.
>
> [1] http://ovirt.org
>
> ---
> Makefile | 2 +
> tools/edit-livecd | 350 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 352 insertions(+), 0 deletions(-)
> create mode 100755 tools/edit-livecd
>
> diff --git a/Makefile b/Makefile
> index 991b27b..62e7fd8 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -25,6 +25,7 @@ install: man
> $(INSTALL_PROGRAM) -D tools/image-creator $(DESTDIR)/usr/bin/image-creator
> $(INSTALL_PROGRAM) -D tools/livecd-iso-to-disk.sh $(DESTDIR)/usr/bin/livecd-iso-to-disk
> $(INSTALL_PROGRAM) -D tools/livecd-iso-to-pxeboot.sh $(DESTDIR)/usr/bin/livecd-iso-to-pxeboot
> + $(INSTALL_PROGRAM) -D tools/edit-livecd $(DESTDIR)/usr/bin/edit-livecd
> $(INSTALL_DATA) -D AUTHORS $(DESTDIR)/usr/share/doc/livecd-tools-$(VERSION)/AUTHORS
> $(INSTALL_DATA) -D COPYING $(DESTDIR)/usr/share/doc/livecd-tools-$(VERSION)/COPYING
> $(INSTALL_DATA) -D README $(DESTDIR)/usr/share/doc/livecd-tools-$(VERSION)/README
> @@ -40,6 +41,7 @@ uninstall:
> rm -f $(DESTDIR)/usr/bin/livecd-creator
> rm -rf $(DESTDIR)/usr/lib/livecd-creator
> rm -rf $(DESTDIR)/usr/share/doc/livecd-tools-$(VERSION)
> + rm -f $(DESTDIR)/usr/bin/edit-livecd
>
> dist : all
> git archive --format=tar --prefix=livecd-tools-$(VERSION)/ HEAD | bzip2 -9v> livecd-tools-$(VERSION).tar.bz2
> diff --git a/tools/edit-livecd b/tools/edit-livecd
> new file mode 100755
> index 0000000..920522c
> --- /dev/null
> +++ b/tools/edit-livecd
> @@ -0,0 +1,350 @@
> +#!/usr/bin/python -tt
> +#
> +# edit livecd: Edit a livecd to insert files
> +#
> +# Copyright 2009, Red Hat Inc.
> +# Written by Perry Myers<pmyers at redhat.com> & David Huff<dhuff at redhat.com>
> +#
> +#
> +# This program is free software; you can redistribute it and/or modify
> +# it under the terms of the GNU General Public License as published by
> +# the Free Software Foundation; version 2 of the License.
> +#
> +# This program is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> +# GNU Library General Public License for more details.
> +#
> +# 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
> +
> +import os
> +import sys
> +import tempfile
> +import shutil
> +import subprocess
> +import optparse
> +import logging
> +
> +from imgcreate.debug import *
> +from imgcreate.fs import *
> +from imgcreate.live import *
> +
> +class ExistingSparseLoopbackDisk(SparseLoopbackDisk):
> + """don't want to expand the disk"""
> + def __init__(self, lofile, size):
> + SparseLoopbackDisk.__init__(self, lofile, size)
> +
> + def create(self):
> + #self.expand(create = True)
> + LoopbackDisk.create(self)
> +
> +class LiveImageEditor(LiveImageCreator):
> + """class for editing LiveCD images.
> +
> + We need an instance of LiveImageCreator however we do not have a kickstart
> + file nor do we need to create a new image. We just want to reuse some of
> + LiveImageCreators methods on an existing livecd image.
> +
> + """
> +
> + def __init__(self, name):
> + """Initialize a LiveImageEditor instance.
> +
> + creates a dummy instance of LiveImageCreator
> + We do not initialize any sub classes b/c we have no ks file.
> +
> + """
> + self.name = name
> +
> + self.tmpdir = "/var/tmp"
> + """The directory in which all temporary files will be created."""
> +
> + self.skip_compression = False
> + """Controls whether to use squashfs to compress the image."""
> +
> + self.skip_minimize = False
> + """Controls whether an image minimizing snapshot should be created."""
> +
> + self._isofstype = "iso9660"
> + self.__isodir = None
> +
> + self._ImageCreator__builddir = None
> + """working directory"""
> +
> + self._ImageCreator_instroot = None
> + """where the extfs.img is mounted for modification"""
> +
> + self._ImageCreator_outdir = None
> + """where final iso gets written"""
> +
> + self._ImageCreator__bindmounts = []
> +
> + self._LoopImageCreator__imagedir = None
> + """dir for the extfs.img"""
> +
> + self._LoopImageCreator__blocksize = 4096
> + self._LoopImageCreator__fslabel = None
> + self._LoopImageCreator__instloop = None
> + self._LoopImageCreator__fstype = None
> + self._LoopImageCreator__image_size = None
> +
> + self._LiveImageCreatorBase__isodir = None
> + """directory where the iso is staged"""
> +
> + # properties
> + def __get_image(self):
> + if self._LoopImageCreator__imagedir is None:
> + self.__ensure_builddir()
> + self._LoopImageCreator__imagedir = tempfile.mkdtemp(dir = os.path.abspath(self.tmpdir), prefix = self.name + "-")
> + return self._LoopImageCreator__imagedir + "/ext3fs.img"
> + _image = property(__get_image)
> + """The location of the image file"""
> +
> +
> + def _get_fstype(self):
> + dev_null = os.open("/dev/null", os.O_WRONLY)
> + try:
> + out = subprocess.Popen(["/sbin/blkid", self._image],
> + stdout = subprocess.PIPE,
> + stderr = dev_null).communicate()[0]
> + for word in out.split():
> + if word.startswith("TYPE"):
> + self._LoopImageCreator__fstype = word.split("=")[1].strip("\"")
> +
> + except IOError, e:
> + raise CreatorError("Failed to determine fsimage TYPE: %s" % e )
> +
> +
> + def _get_fslable(self):
> + dev_null = os.open("/dev/null", os.O_WRONLY)
> + try:
> + out = subprocess.Popen(["/sbin/e2label", self._image],
> + stdout = subprocess.PIPE,
> + stderr = dev_null).communicate()[0]
> +
> + self._LoopImageCreator__fslable = out.strip()
> +
> + except IOError, e:
> + raise CreatorError("Failed to determine fsimage TYPE: %s" % e )
> +
> +
> + def __ensure_builddir(self):
> + if not self._ImageCreator__builddir is None:
> + return
> +
> + try:
> + self._ImageCreator__builddir = tempfile.mkdtemp(dir = os.path.abspath(self.tmpdir),
> + prefix = "edit-livecd-")
> + except OSError, (err, msg):
> + raise CreatorError("Failed create build directory in %s: %s" %
> + (self.tmpdir, msg))
> +
> +
> + def _run_script(self, script):
> +
> + (fd, path) = tempfile.mkstemp(prefix = "script-",
> + dir = self._instroot + "/tmp")
> +
> + logging.debug("copying script to install root: %s" % path)
> + shutil.copy(os.path.abspath(script), path)
> + os.close(fd)
> + os.chmod(path, 0700)
> +
> + script = "/tmp/" + os.path.basename(path)
> +
> +
> + try:
> + subprocess.call([script], preexec_fn = self._chroot)
> + except OSError, e:
> + raise CreatorError("Failed to execute script %s, %s " % (script, e))
> + finally:
> + os.unlink(path)
> +
> +
> + def mount(self, base_on, cachedir = None):
> + """mount existing file system.
> +
> + we have to override mount b/c we are not creating an new install root
> + nor do we need to setup the file system, ie makedirs(/etc/, /boot, ...),
> + nor do we want to overwrite fstab, or create selinuxfs
> +
> + We also need to get some info about the image before we
> + can mount it.
> +
> + """
> +
> + if not base_on:
> + raise CreatorError("No base livecd image specified")
> +
> + self.__ensure_builddir()
> +
> + self._ImageCreator_instroot = self._ImageCreator__builddir + "/install_root"
> + self._LoopImageCreator__imagedir = self._ImageCreator__builddir + "/ex"
> + self._ImageCreator_outdir = self._ImageCreator__builddir + "/out"
> +
> + makedirs(self._ImageCreator_instroot)
> + makedirs(self._LoopImageCreator__imagedir)
> + makedirs(self._ImageCreator_outdir)
> +
> + LiveImageCreator._base_on(self, base_on)
> +
> + self._LoopImageCreator__image_size = os.stat(self._image)[stat.ST_SIZE]
> + self._get_fstype()
> + self._get_fslable()
> + self.fslabel = self._LoopImageCreator__fslable
> +
> + self._LoopImageCreator__instloop = ExtDiskMount(ExistingSparseLoopbackDisk(self._image,
> + self._LoopImageCreator__image_size),
> + self._ImageCreator_instroot,
> + self._fstype,
> + self._LoopImageCreator__blocksize,
> + self.fslabel)
> + try:
> + self._LoopImageCreator__instloop.mount()
> + except MountError, e:
> + raise CreatorError("Failed to loopback mount '%s' : %s" %
> + (self._image, e))
> +
> + cachesrc = cachedir or (self._ImageCreator__builddir + "/yum-cache")
> + makedirs(cachesrc)
> +
> + for (f, dest) in [("/sys", None), ("/proc", None),
> + ("/dev/pts", None), ("/dev/shm", None),
> + (cachesrc, "/var/cache/yum")]:
> + self._ImageCreator__bindmounts.append(BindChrootMount(f, self._instroot, dest))
> +
> + self._do_bindmounts()
> +
> + os.symlink("../proc/mounts", self._instroot + "/etc/mtab")
> +
> + self.__copy_cd_root(base_on)
> +
> +
> + def __copy_cd_root(self, base_on):
> + """helper function to root content of the base liveCD to ISOdir"""
> +
> + isoloop = DiskMount(LoopbackDisk(base_on, 0), self._mkdtemp())
> + self._LiveImageCreatorBase__isodir = self._ImageCreator__builddir + "/iso"
> +
> + try:
> + isoloop.mount()
> + # legacy LiveOS filesystem layout support, remove for F9 or F10
> + if os.path.exists(isoloop.mountdir + "/squashfs.img"):
> + squashimg = isoloop.mountdir + "/squashfs.img"
> + else:
> + squashimg = isoloop.mountdir + "/LiveOS/squashfs.img"
> +
> + #copy over everything but squashimg
> + shutil.copytree(isoloop.mountdir,
> + self._LiveImageCreatorBase__isodir,
> + ignore=shutil.ignore_patterns("squashfs.img", "osmin.img"))
> + except MountError, e:
> + raise CreatorError("Failed to loopback mount '%s' : %s" %
> + (base_on, e))
> +
> + finally:
> + isoloop.cleanup()
> +
> +
> +def parse_options(args):
> + parser = optparse.OptionParser(usage = "%prog [-s=<script.sh>]<LIVECD.iso>")
> +
> + parser.add_option("-n", "--name", type="string", dest="name",
> + help="name of new livecd (don't include .iso will be added)")
> +
> + parser.add_option("-o", "--output", type="string", dest="output",
> + help="specify the output dir")
> +
> + parser.add_option("-s", "--script", type="string", dest="script",
> + help="specify script to run chrooted in the livecd fsimage")
> +
> + parser.add_option("-t", "--tmpdir", type="string",
> + dest="tmpdir", default="/var/tmp",
> + help="Temporary directory to use (default: /var/tmp)")
> +
> + parser.add_option("", "--skip-compression", action="store_true", dest="skip_compression")
> +
> + parser.add_option("", "--skip-minimize", action="store_true", dest="skip_minimize")
> +
> + setup_logging(parser)
> +
> + (options, args) = parser.parse_args()
> +
> + if len(args) != 1:
> + parser.print_usage()
> + sys.exit(1)
> +
> + return (args[0], options)
> +
> +def rebuild_iso_symlinks(isodir):
> + # remove duplicate files and rebuild symlinks to reduce iso size
> + efi_vmlinuz = "%s/EFI/boot/vmlinuz0" % isodir
> + isolinux_vmlinuz = "%s/isolinux/vmlinuz0" % isodir
> + efi_initrd = "%s/EFI/boot/initrd0.img" % isodir
> + isolinux_initrd = "%s/isolinux/initrd0.img" % isodir
> +
> + os.remove(efi_vmlinuz)
> + os.remove(efi_initrd)
> + os.symlink(isolinux_vmlinuz,efi_vmlinuz)
> + os.symlink(isolinux_initrd,efi_initrd)
> +
> +
> +def main():
> + (livecd, options) = parse_options(sys.argv[1:])
> +
> + if os.geteuid () != 0:
> + print>> sys.stderr, "You must run edit-livecd as root"
> + return 1
> +
> + if options.name:
> + name = options.name
> + else:
> + name = os.path.basename(livecd) + ".edited"
> +
> + if options.output:
> + output = options.output
> + else:
> + output = os.path.dirname(livecd)
> +
> +
> + editor = LiveImageEditor(name)
> + editor.tmpdir = os.path.abspath(options.tmpdir)
> + editor.skip_compression = options.skip_compression
> + editor.skip_minimize = options.skip_minimize
> +
> + try:
> + editor.mount(livecd, cachedir = None)
> + if options.script:
> + print "Running edit script '%s'" % options.script
> + editor._run_script(options.script)
> + else:
> + print "Launching shell. Exit to continue."
> + print "----------------------------------"
> + editor.launch_shell()
> + rebuild_iso_symlinks(editor._LiveImageCreatorBase__isodir)
> + editor.unmount()
> + editor.package(output)
> + except CreatorError, e:
> + logging.error(u"Error editing Live CD : %s" % e)
> + return 1
> + finally:
> + editor.cleanup()
> +
> + return 0
> +
> +
> +if __name__ == "__main__":
> + sys.exit(main())
> +
> +
> +arch = rpmUtils.arch.getBaseArch()
> +if arch in ("i386", "x86_64"):
> + LiveImageCreator = x86LiveImageCreator
> +elif arch in ("ppc",):
> + LiveImageCreator = ppcLiveImageCreator
> +elif arch in ("ppc64",):
> + LiveImageCreator = ppc64LiveImageCreator
> +else:
> + raise CreatorError("Architecture not supported!")
More information about the livecd
mailing list