[livemedia-creator] Add filesystem image install support

Brian C. Lane bcl at redhat.com
Thu Feb 7 18:51:47 UTC 2013


From: "Brian C. Lane" <bcl at redhat.com>

This adds support for installing to a filesystem image instead of a
partitioned disk image. It requires Anaconda's --dirinstall support.
Also re-organized the code to break it up into smaller methods.

This speeds up iso creation in no-virt mode by removing the need to copy
the filesystem from the partitioned disk image to the filesystem image
that is used to make the squashfs image.
---
 README.livemedia-creator   |  44 ++--
 src/sbin/livemedia-creator | 559 ++++++++++++++++++++++++++-------------------
 2 files changed, 355 insertions(+), 248 deletions(-)

diff --git a/README.livemedia-creator b/README.livemedia-creator
index dee51b2..424bd24 100644
--- a/README.livemedia-creator
+++ b/README.livemedia-creator
@@ -2,7 +2,9 @@
 INTRO
 -----
 livemedia-creator uses Anaconda, kickstart and Lorax to create bootable media
-such as live iso's that use the same install path as a normal system install.
+that use the same install path as a normal system install. It can be used to
+make live isos, bootable (partitioned) disk images and filesystem images for
+use with virtualization.
 
 The general idea is to use virt-install to install into a disk image and then
 use the disk image to create the bootable media.
@@ -18,12 +20,12 @@ minimum you need:
 QUICKSTART
 ----------
 sudo livemedia-creator --make-iso \
---iso=/extra/iso/Fedora-16-x86_64-netinst.iso --ks=./fedora-livemedia.ks
+--iso=/extra/iso/Fedora-18-x86_64-netinst.iso --ks=./docs/fedora-livemedia.ks
 
 If you are using the lorax git repo you can run it like so:
 
 sudo PATH=./src/sbin/:$PATH PYTHONPATH=./src/ ./src/sbin/livemedia-creator \
---make-iso --iso=/extra/iso/Fedora-16-x86_64-netinst.iso \
+--make-iso --iso=/extra/iso/Fedora-18-x86_64-netinst.iso \
 --ks=./docs/fedora-livemedia.ks --lorax-templates=./share/
 
 If you want to watch the install you can pass '--vnc vnc' and use a vnc
@@ -35,16 +37,17 @@ to monitor the logs for fatal errors, but may not catch everything.
 
 HOW IT WORKS
 ------------
-The --make-* switches define the final output.
-
-You then need to either pass --iso and --ks in order to create a disk image
-using virt-install, or --disk-image to use a disk image from a previous run
-to create the .iso
-
-virt-install boots using the passed Anaconda installer iso and installs the
-system based on the kickstart. The %post section of the kickstart is used to
-customize the installed system in the same way that current spin-kickstarts
-do.
+There are 2 stages, the install stage which produces a disk or filesystem
+image as its output, and the boot media creation which uses the image as
+its input. Normally you would have it run both stages, but it is possible
+to have it stop after the install stage, using --image-only, or to have it
+skip the install stage and use a previously created disk image by passing
+--disk-image or --fs-image
+
+When creating an iso virt-install boots using the passed Anaconda installer iso
+and installs the system based on the kickstart. The %post section of the
+kickstart is used to customize the installed system in the same way that
+current spin-kickstarts do.
 
 livemedia-creator monitors the install process for problems by watching the
 install logs. They are written to the current directory or to the base
@@ -129,7 +132,7 @@ it as well.
 ANACONDA IMAGE INSTALL
 ----------------------
 You can create images without using virt-install by passing --no-virt on the
-cmdline. This will use Anaconda's image install feature to handle the install.
+cmdline. This will use Anaconda's directory install feature to handle the install.
 There are a couple of things to keep in mind when doing this:
 
 1. It will be most reliable when building images for the same release that the
@@ -168,10 +171,11 @@ This will produce an ami-root.img file in the working directory.
 At this time I have not tested the image with EC2. Feedback would we welcome.
 
 
-APPLIANCE CREATION ------------------ livemedia-creator can now replace
-appliance-tools by using the --make-appliance switch. This will create the
-partitioned disk image and an XML file that can be used with virt-image to
-setup a virtual system.
+APPLIANCE CREATION
+------------------
+livemedia-creator can now replace appliance-tools by using the --make-appliance
+switch. This will create the partitioned disk image and an XML file that can be
+used with virt-image to setup a virtual system.
 
 The XML is generated using the Mako template from
 /usr/share/lorax/appliance/virt-image.xml You can use a different template by
@@ -218,7 +222,9 @@ disk image. Or you can pass --keep-image to keep it around after lorax is
 run.
 
 Cleaning up aborted --no-virt installs can sometimes be accomplished by running
-the anaconda-cleanup script.
+the anaconda-cleanup script. As of f18 anaconda is multi-threaded and it can
+sometimes become stuck and refuse to exit. When this happens you can usually
+clean up by first killing the anaconda process then running anaconda-cleanup.
 
 
 HACKING
diff --git a/src/sbin/livemedia-creator b/src/sbin/livemedia-creator
index f80ad00..202e963 100755
--- a/src/sbin/livemedia-creator
+++ b/src/sbin/livemedia-creator
@@ -2,7 +2,7 @@
 #
 # Live Media Creator
 #
-# Copyright (C) 2011-2012  Red Hat, Inc.
+# Copyright (C) 2011-2013  Red Hat, Inc.
 #
 # 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
@@ -34,7 +34,6 @@ import threading
 import SocketServer
 from time import sleep
 import shutil
-import traceback
 import argparse
 import hashlib
 
@@ -49,9 +48,10 @@ from mako.exceptions import text_error_template
 # Use the Lorax treebuilder branch for iso creation
 from pylorax.base import DataHolder
 from pylorax.treebuilder import TreeBuilder, RuntimeBuilder, udev_escape
-from pylorax.sysutils import joinpaths, remove, linktree
+from pylorax.sysutils import joinpaths, remove
 from pylorax.imgutils import PartitionMount, mksparse, mkext4img, loop_detach
-from pylorax.imgutils import get_loop_name, dm_detach
+from pylorax.imgutils import get_loop_name, dm_detach, mount, umount, Mount
+from pylorax.imgutils import mksquashfs
 from pylorax.executils import execWithRedirect, execWithCapture
 
 # no-virt mode doesn't need libvirt, so make it optional
@@ -64,6 +64,12 @@ except ImportError:
 DRACUT_DEFAULT = ["--xz", "--add", "livenet dmsquash-live convertfs pollcdrom",
                   "--omit", "plymouth"]
 
+ROOT_PATH = "/mnt/sysimage/"
+RUNTIME = "images/install.img"
+
+class InstallError(Exception):
+    pass
+
 
 class LogRequestHandler(SocketServer.BaseRequestHandler):
     """
@@ -184,11 +190,7 @@ class IsoMountpoint(object):
         self.initrd_path = initrd_path
 
         if not self.initrd_path:
-            self.mount_dir = tempfile.mkdtemp()
-            if not self.mount_dir:
-                raise Exception("Error creating temporary directory")
-
-            execWithRedirect("mount", ["-o", "loop", self.iso_path, self.mount_dir])
+            self.mount_dir = mount(self.iso_path, opts="loop")
         else:
             self.mount_dir = self.initrd_path
 
@@ -213,8 +215,7 @@ class IsoMountpoint(object):
 
     def umount( self ):
         if not self.initrd_path:
-            execWithRedirect("umount", [self.mount_dir])
-            os.rmdir( self.mount_dir )
+            umount(self.mount_dir)
 
     def get_iso_label( self ):
         """
@@ -328,8 +329,8 @@ class VirtualInstall( object ):
         Could use libvirt for this instead.
         """
         log.info( "Shutting down {0}".format(self.virt_name) )
-        subprocess.call(["virsh","destroy",self.virt_name])
-        subprocess.call(["virsh","undefine",self.virt_name])
+        subprocess.call(["virsh", "destroy", self.virt_name])
+        subprocess.call(["virsh", "undefine", self.virt_name])
 
 def is_image_mounted(disk_img):
     """
@@ -343,42 +344,37 @@ def is_image_mounted(disk_img):
     return False
 
 
-def anaconda_install( disk_img, disk_size, kickstart, repo, args ):
+class KernelInfo(object):
     """
-    disk_img    Full path of the disk image
-    disk_size   Disk size in GB
-    kickstart   Full path to kickstart file
-    repo        URL of repository
-    args        Extra args to pass to anaconda --image install
+    Info about the kernels in boot_dir
     """
-    import selinux
-
-    # Set selinux to Permissive if it is Enforcing
-    selinux_enforcing = False
-    if selinux.is_selinux_enabled() and selinux.security_getenforce():
-        selinux_enforcing = True
-        selinux.security_setenforce(0)
-
-    # Create the sparse image
-    mksparse( disk_img, disk_size * 1024**3 )
-
-    cmd = [ "anaconda", "--image", disk_img, "--kickstart", kickstart,
-            "--cmdline", "--repo", repo_url ]
-    cmd += args
-
-    rc = execWithRedirect( cmd[0], cmd[1:] )
+    def __init__(self, boot_dir):
+        self.boot_dir = boot_dir
+        self.list = self.get_kernels()
+        self.arch = self.get_kernel_arch()
+        log.debug("kernel_list for {0.boot_dir} = {0.list}".format(self))
+        log.debug("kernel_arch is {0.arch}".format(self))
+
+    def get_kernels(self):
+        """
+        Get a list of the kernels in the boot_dir
 
-    if selinux_enforcing:
-        selinux.security_setenforce(1)
-    return rc
+        Examine the vmlinuz-* versions and return a list of them
+        """
+        files = os.listdir(self.boot_dir)
+        return [f[8:] for f in files if f.startswith("vmlinuz-")]
 
+    def get_kernel_arch(self):
+        """
+        Get the arch of the first kernel in boot_dir
 
-def get_kernels( boot_dir ):
-    """
-    Examine the vmlinuz-* versions and return a list of them
-    """
-    files = os.listdir(boot_dir)
-    return [f[8:] for f in files if f.startswith("vmlinuz-")]
+        Defaults to i386
+        """
+        if self.list:
+            kernel_arch = self.list[0].split(".")[-1]
+        else:
+            kernel_arch = "i386"
+        return kernel_arch
 
 
 def make_appliance(disk_img, name, template, outfile, networks=None, ram=1024,
@@ -450,8 +446,32 @@ def make_ami( disk_img, ami_img="ami-root.img", ami_label="AMI" ):
     return work_dir
 
 
-def make_livecd( disk_img, squashfs_args="", templatedir=None,
-                 title="Linux", project="Linux", releasever=16, isolabel=None ):
+def make_runtime(opts, mount_dir, work_dir):
+    """
+    Make the squashfs image from a directory
+
+    Result is in work_dir+RUNTIME
+    """
+    kernels = KernelInfo(joinpaths(mount_dir, "boot" ))
+
+    # Fake yum object
+    fake_yum = DataHolder(conf=DataHolder(installroot=mount_dir))
+    # Fake arch with only basearch set
+    arch = DataHolder(basearch=kernels.arch, libdir=None, buildarch=None)
+    # TODO: Need to get release info from someplace...
+    product = DataHolder(name=opts.project, version=opts.releasever, release="",
+                            variant="", bugurl="", isfinal=False)
+
+    # This is a mounted image partition, cannot hardlink to it, so just use it
+    # symlink mount_dir/images to work_dir/images so we don't run out of space
+    os.makedirs(joinpaths(work_dir, "images"))
+
+    rb = RuntimeBuilder(product, arch, fake_yum)
+    log.info("Creating runtime")
+    rb.create_runtime(joinpaths(work_dir, RUNTIME), size=None)
+
+
+def make_livecd(opts, mount_dir, work_dir):
     """
     Take the content from the disk image and make a livecd out of it
 
@@ -466,76 +486,195 @@ def make_livecd( disk_img, squashfs_args="", templatedir=None,
     mounts that and then mounts LiveOS/rootfs.img as /
 
     """
-    with PartitionMount( disk_img ) as img_mount:
-        if not img_mount or not img_mount.mount_dir:
-            return None
+    kernels = KernelInfo(joinpaths(mount_dir, "boot" ))
+
+    arch = DataHolder(basearch=kernels.arch, libdir=None, buildarch=None)
+    # TODO: Need to get release info from someplace...
+    product = DataHolder(name=opts.project, version=opts.releasever, release="",
+                            variant="", bugurl="", isfinal=False)
+
+    # Link /images to work_dir/images to make the templates happy
+    if os.path.islink(joinpaths(mount_dir, "images")):
+        os.unlink(joinpaths(mount_dir, "images"))
+    execWithRedirect("/bin/ln", ["-s", joinpaths(work_dir, "images"),
+                                 joinpaths(mount_dir, "images")])
+
+    # The templates expect the config files to be in /tmp/config_files
+    # I think these should be release specific, not from lorax, but for now
+    configdir = joinpaths(opts.lorax_templates,"live/config_files/")
+    configdir_path = "tmp/config_files"
+    fullpath = joinpaths(mount_dir, configdir_path)
+    if os.path.exists(fullpath):
+        remove(fullpath)
+    shutil.copytree(configdir, fullpath)
+
+    isolabel = opts.volid or "{0.name} {0.version} {1.basearch}".format(product, arch)
+    if len(isolabel) > 32:
+        isolabel = isolabel[:32]
+        log.warn("Truncating isolabel to 32 chars: %s" % (isolabel,))
+
+    tb = TreeBuilder(product=product, arch=arch,
+                     inroot=mount_dir, outroot=work_dir,
+                     runtime=RUNTIME, isolabel=isolabel,
+                     templatedir=joinpaths(opts.lorax_templates,"live/"))
+    log.info( "Rebuilding initrds" )
+    if not opts.dracut_args:
+        dracut_args = DRACUT_DEFAULT
+    else:
+        dracut_args = []
+        for arg in opts.dracut_args:
+            dracut_args += arg.split(" ", 1)
+    log.info("dracut args = {0}".format(dracut_args))
+    tb.rebuild_initrds(add_args=dracut_args)
+    log.info("Building boot.iso")
+    tb.build()
 
-        kernel_list = get_kernels( joinpaths( img_mount.mount_dir, "boot" ) )
-        log.debug( "kernel_list = {0}".format(kernel_list) )
-        if kernel_list:
-            kernel_arch = kernel_list[0].split(".")[-1]
-        else:
-            kernel_arch = "i386"
-        log.debug( "kernel_arch = {0}".format(kernel_arch) )
+    return work_dir
 
-        work_dir = tempfile.mkdtemp()
-        log.info( "working dir is {0}".format(work_dir) )
-
-        runtime = "images/install.img"
-        # This is a mounted image partition, cannot hardlink to it, so just use it
-        installroot = img_mount.mount_dir
-        # symlink installroot/images to work_dir/images so we don't run out of space
-        os.makedirs( joinpaths( work_dir, "images" ) )
-
-        # TreeBuilder expects the config files to be in /tmp/config_files
-        # I think these should be release specific, not from lorax, but for now
-        configdir = joinpaths(templatedir,"live/config_files/")
-        configdir_path = "tmp/config_files"
-        fullpath = joinpaths(installroot, configdir_path)
-        if os.path.exists(fullpath):
-            remove(fullpath)
-        shutil.copytree(configdir, fullpath)
-
-        # Fake yum object
-        fake_yum = DataHolder( conf=DataHolder( installroot=installroot ) )
-        # Fake arch with only basearch set
-        arch = DataHolder( basearch=kernel_arch, libdir=None, buildarch=None )
-        # TODO: Need to get release info from someplace...
-        product = DataHolder( name=project, version=releasever, release="",
-                                variant="", bugurl="", isfinal=False )
-
-        rb = RuntimeBuilder( product, arch, fake_yum )
-        log.info( "Creating runtime" )
-        rb.create_runtime( joinpaths( work_dir, runtime ), size=None )
-
-        # Link /images to work_dir/images to make the templates happy
-        if os.path.islink( joinpaths( installroot, "images" ) ):
-            os.unlink( joinpaths( installroot, "images" ) )
-        execWithRedirect("/bin/ln", ["-s", joinpaths(work_dir, "images"),
-                                     joinpaths(installroot, "images")])
-
-        isolabel = isolabel or "{0.name} {0.version} {1.basearch}".format(product, arch)
-        if len(isolabel) > 32:
-            isolabel = isolabel[:32]
-            log.error("Truncating isolabel to 32 chars: %s" % (isolabel,))
-
-        tb = TreeBuilder( product=product, arch=arch,
-                          inroot=installroot, outroot=work_dir,
-                          runtime=runtime, isolabel=isolabel,
-                          templatedir=joinpaths(templatedir,"live/"))
-        log.info( "Rebuilding initrds" )
-        if not opts.dracut_args:
-            dracut_args = DRACUT_DEFAULT
+
+def novirt_install(opts, disk_img, disk_size, repo_url):
+    """
+    Use Anaconda to install to a disk image
+    """
+    import selinux
+
+    # Set selinux to Permissive if it is Enforcing
+    selinux_enforcing = False
+    if selinux.is_selinux_enabled() and selinux.security_getenforce():
+        selinux_enforcing = True
+        selinux.security_setenforce(0)
+
+    cmd = ["anaconda", "--kickstart", opts.ks[0], "--cmdline", "--repo", repo_url]
+    if opts.anaconda_args:
+        for arg in opts.anaconda_args:
+            cmd += arg.split(" ", 1)
+    if opts.proxy:
+        cmd += ["--proxy", opts.proxy]
+    if opts.armplatform:
+        cmd += ["--armplatform", opts.armplatform]
+
+    if opts.make_iso or opts.make_fsimage:
+        # Make a blank fs image
+        cmd += ["--dirinstall"]
+
+        mkext4img(None, disk_img, label="Anaconda", size=disk_size * 1024**3)
+        if not os.path.isdir(ROOT_PATH):
+            os.mkdir(ROOT_PATH)
+        mount(disk_img, opts="loop", mnt=ROOT_PATH)
+    else:
+        cmd += ["--image", disk_img]
+
+        # Create the sparse image
+        mksparse( disk_img, disk_size * 1024**3 )
+
+    rc = execWithRedirect(cmd[0], cmd[1:])
+
+    # Move the anaconda logs over to a log directory
+    log_dir = os.path.abspath(os.path.dirname(opts.logfile))
+    log_anaconda = joinpaths(log_dir, "anaconda")
+    if not os.path.isdir(log_anaconda):
+        os.mkdir(log_anaconda)
+    for l in ["anaconda.log", "ifcfg.log", "program.log", "storage.log",
+              "packaging.log", "yum.log"]:
+        if os.path.exists("/tmp/"+l):
+            shutil.copy2("/tmp/"+l, log_anaconda)
+            os.unlink("/tmp/"+l)
+
+    if opts.make_iso or opts.make_fsimage:
+        umount(ROOT_PATH)
+    else:
+        # If anaconda failed the disk image may still be in use by dm
+        execWithRedirect("anaconda-cleanup", [])
+        dm_name = os.path.splitext(os.path.basename(disk_img))[0]
+        dm_path = "/dev/mapper/"+dm_name
+        if os.path.exists(dm_path):
+            dm_detach(dm_path)
+            loop_detach(get_loop_name(disk_img))
+
+    if selinux_enforcing:
+        selinux.security_setenforce(1)
+
+    if rc:
+        raise InstallError("novirt_install failed")
+
+
+def virt_install(opts, install_log, disk_img, disk_size):
+    """
+    Use virt-install to install to a disk image
+    """
+    iso_mount = IsoMountpoint(opts.iso, opts.location)
+    log_monitor = LogMonitor(install_log)
+
+    kernel_args = ""
+    if opts.kernel_args:
+        kernel_args += opts.kernel_args
+    if opts.proxy:
+        kernel_args += " proxy="+opts.proxy
+
+    virt = VirtualInstall(iso_mount, opts.ks, disk_img, disk_size,
+                           kernel_args, opts.ram, opts.vnc, opts.arch,
+                           log_check = log_monitor.server.log_check,
+                           virtio_host = log_monitor.host,
+                           virtio_port = log_monitor.port)
+    virt.destroy()
+    log_monitor.shutdown()
+    iso_mount.umount()
+
+    if log_monitor.server.log_check():
+        raise InstallError("virt_install failed")
+
+
+def make_squashfs(disk_img, work_dir, compression="xz"):
+    """
+    Take disk_img and put it into LiveOS/rootfs.img and squashfs this
+    tree into work_dir+RUNTIME
+    """
+    liveos_dir = joinpaths(work_dir, "runtime/LiveOS")
+    os.makedirs(liveos_dir)
+    os.makedirs(os.path.dirname(joinpaths(work_dir, RUNTIME)))
+    execWithRedirect("/bin/ln", [disk_img,
+                     joinpaths(liveos_dir, "rootfs.img")])
+    mksquashfs(joinpaths(work_dir, "runtime"),
+               joinpaths(work_dir, RUNTIME), compression)
+    remove(joinpaths(work_dir, "runtime"))
+
+
+def make_image(opts, ks):
+    """
+    Install to an image
+
+    Use virt or anaconda to install to an image.
+
+    Returns the name of the image created.
+    """
+    disk_size = 1 + (sum([p.size for p in ks.handler.partition.partitions]) / 1024)
+    log.info("disk_size = {0}GB".format(disk_size))
+
+    repo_url = ks.handler.method.url
+
+    if opts.image_name:
+        disk_img = joinpaths(opts.tmp, opts.image_name)
+    else:
+        disk_img = tempfile.mktemp(prefix="disk", suffix=".img", dir=opts.tmp)
+    install_log = os.path.abspath(os.path.dirname(opts.logfile))+"/virt-install.log"
+
+    log.info("disk_img = {0}".format(disk_img))
+    log.info("install_log = {0}".format(install_log))
+
+    try:
+        if opts.no_virt:
+            novirt_install(opts, disk_img, disk_size, repo_url)
         else:
-            dracut_args = []
-            for arg in opts.dracut_args:
-                dracut_args += arg.split(" ", 1)
-        log.info( "dracut args = {0}".format( dracut_args ) )
-        tb.rebuild_initrds( add_args=dracut_args )
-        log.info( "Building boot.iso" )
-        tb.build()
+            virt_install(opts, install_log, disk_img, disk_size)
+    except InstallError as e:
+        log.error("Install failed: {0}".format(e))
+        if not opts.keep_image:
+            log.info("Removing bad disk image")
+            os.unlink(disk_img)
+        raise
 
-        return work_dir
+    log.info("Disk Image install successful")
+    return disk_img
 
 
 def setup_logging(opts):
@@ -574,7 +713,9 @@ if __name__ == '__main__':
     action.add_argument( "--make-iso", action="store_true",
                          help="Build a live iso" )
     action.add_argument( "--make-disk", action="store_true",
-                         help="Build a disk image" )
+                         help="Build a partitioned disk image" )
+    action.add_argument( "--make-fsimage", action="store_true",
+                         help="Build a filesystem image" )
     action.add_argument( "--make-appliance", action="store_true",
                          help="Build an appliance image and XML description" )
     action.add_argument( "--make-ami", action="store_true",
@@ -584,13 +725,15 @@ if __name__ == '__main__':
                         help="Anaconda installation .iso path to use for virt-install" )
     parser.add_argument( "--disk-image", type=os.path.abspath,
                         help="Path to disk image to use for creating final image" )
+    parser.add_argument( "--fs-image", type=os.path.abspath,
+                        help="Path to filesystem image to use for creating final image" )
 
     parser.add_argument( "--ks", action="append", type=os.path.abspath,
                          help="Kickstart file defining the install." )
     parser.add_argument( "--image-name", default=None,
-                         help="Name of disk image to create. Default is a random name." )
+                         help="Name of fs/disk image to create. Default is a random name." )
     parser.add_argument( "--image-only", action="store_true",
-                         help="Exit after creating disk image." )
+                         help="Exit after creating fs/disk image." )
     parser.add_argument( "--keep-image", action="store_true",
                          help="Keep raw disk image after .iso creation" )
     parser.add_argument( "--no-virt", action="store_true",
@@ -692,23 +835,30 @@ if __name__ == '__main__':
         log.error( "The disk image {0} is missing.".format( opts.disk_image ) )
         sys.exit( 1 )
 
-    if not opts.no_virt and not opts.iso and not opts.disk_image:
+    if opts.fs_image and not os.path.exists( opts.fs_image ):
+        log.error( "The filesystem image {0} is missing.".format( opts.fs_image ) )
+        sys.exit( 1 )
+
+    is_install = not (opts.disk_image or opts.fs_image)
+    if is_install and not opts.no_virt and not opts.iso:
         log.error( "virt-install needs an install iso." )
         sys.exit( 1 )
 
     if opts.volid and len(opts.volid) > 32:
-        logger.fatal("the volume id cannot be longer than 32 characters")
+        log.fatal("the volume id cannot be longer than 32 characters")
         sys.exit(1)
 
-    if not opts.no_virt and not libvirt:
+    if is_install and not opts.no_virt and not libvirt:
         log.error("virt-install requires libvirt-python to be installed.")
         sys.exit(1)
 
-    if not opts.no_virt and not os.path.exists("/usr/bin/virt-install"):
+    if is_install and not opts.no_virt \
+       and not os.path.exists("/usr/bin/virt-install"):
         log.error("virt-install requires python-virtinst to be installed.")
         sys.exit(1)
 
-    if opts.no_virt and not os.path.exists("/usr/sbin/anaconda"):
+    if not is_install and opts.no_virt \
+       and not os.path.exists("/usr/sbin/anaconda"):
         log.error("no-virt requires anaconda to be installed.")
         sys.exit(1)
 
@@ -721,7 +871,7 @@ if __name__ == '__main__':
                   "exist".format(opts.app_template))
         sys.exit(1)
 
-    if opts.image_name and os.path.exists(joinpaths(opts.tmp,opts.image_name)):
+    if opts.image_name and os.path.exists(joinpaths(opts.tmp, opts.image_name)):
         log.error("The disk image to be created should not exist.")
         sys.exit(1)
 
@@ -737,123 +887,74 @@ if __name__ == '__main__':
         ks = KickstartParser( ks_version, errorsAreFatal=False, missingIncludeIsFatal=False )
         ks.readKickstart( opts.ks[0] )
 
-    # Make the disk image
-    if not opts.disk_image:
+    # Make the disk or filesystem image
+    if not opts.disk_image and not opts.fs_image:
         if not opts.ks:
             log.error("Image creation requires a kickstart file")
             sys.exit(1)
 
-        disk_size = 1 + (sum( [p.size for p in ks.handler.partition.partitions] ) / 1024)
-        log.info( "disk_size = {0}GB".format(disk_size) )
-
+        errors = []
         if ks.handler.method.method != "url":
-            log.error( "Only url install method is currently supported. Please "
-                       "fix your kickstart file." )
-            sys.exit( 1 )
-        repo_url = ks.handler.method.url
-        if ks.handler.displaymode.displayMode is not None:
-            log.error("The kickstart must not set a display mode (text, cmdline, "
-                      "graphical), this will interfere with livemedia-creator.")
-            sys.exit(1)
+            errors.append("Only url install method is currently supported. Please "
+                          "fix your kickstart file." )
 
-        if opts.image_name:
-            disk_img = joinpaths(opts.tmp, opts.image_name)
-        else:
-            disk_img = tempfile.mktemp( prefix="disk", suffix=".img", dir=opts.tmp )
-        install_log = os.path.abspath(os.path.dirname(opts.logfile))+"/virt-install.log"
+        if ks.handler.displaymode.displayMode is not None:
+            errors.append("The kickstart must not set a display mode (text, cmdline, "
+                          "graphical), this will interfere with livemedia-creator.")
 
-        log.info( "disk_img = {0}".format(disk_img) )
-        log.info( "install_log = {0}".format(install_log) )
+        if errors:
+            for e in errors:
+                log.error(e)
+            sys.exit(1)
 
-        if opts.no_virt:
-            anaconda_args = []
-            if opts.anaconda_args:
-                for arg in opts.anaconda_args:
-                    anaconda_args += arg.split(" ", 1)
-            if opts.proxy:
-                anaconda_args += [ "--proxy", opts.proxy ]
-            if opts.armplatform:
-                anaconda_args += [ "--armplatform", opts.armplatform ]
-
-            # Use anaconda's image install
-            install_error = anaconda_install( disk_img, disk_size, opts.ks[0],
-                                              repo_url, anaconda_args )
-
-            # Move the anaconda logs over to a log directory
-            log_dir = os.path.abspath(os.path.dirname(opts.logfile))
-            log_anaconda = joinpaths( log_dir, "anaconda" )
-            if not os.path.isdir( log_anaconda ):
-                os.mkdir( log_anaconda )
-            for l in ["anaconda.log", "ifcfg.log", "program.log", "storage.log",
-                      "packaging.log", "yum.log"]:
-                if os.path.exists( "/tmp/"+l ):
-                    shutil.copy2( "/tmp/"+l, log_anaconda )
-                    os.unlink( "/tmp/"+l )
-
-            # If anaconda failed the disk image may still be in use by dm
-            execWithRedirect("anaconda-cleanup", [])
-            dm_name = os.path.splitext(os.path.basename(disk_img))[0]
-            dm_path = "/dev/mapper/"+dm_name
-            if os.path.exists(dm_path):
-                dm_detach(dm_path)
-                loop_detach(get_loop_name(disk_img))
+        # Make the image. Output of this is either a partitioned disk image or a fsimage
+        try:
+            disk_img = make_image(opts, ks)
+        except InstallError as e:
+            log.error("ERROR: Image creation failed: %s" % (e))
+            sys.exit(1)
 
-        else:
-            iso_mount = IsoMountpoint( opts.iso, opts.location )
-            log_monitor = LogMonitor( install_log )
-
-            kernel_args = ""
-            if opts.kernel_args:
-                kernel_args += opts.kernel_args
-            if opts.proxy:
-                kernel_args += " proxy="+opts.proxy
-
-            virt = VirtualInstall( iso_mount, opts.ks, disk_img, disk_size,
-                                   kernel_args, opts.ram, opts.vnc, opts.arch,
-                                   log_check = log_monitor.server.log_check,
-                                   virtio_host = log_monitor.host,
-                                   virtio_port = log_monitor.port )
-            virt.destroy()
-            log_monitor.shutdown()
-            iso_mount.umount()
-
-            install_error = log_monitor.server.log_check()
-
-        if install_error:
-            log.error( "Install failed" )
-            if not opts.keep_image:
-                log.info( "Removing bad disk image" )
-                os.unlink( disk_img )
-            sys.exit( 1 )
-        else:
-            log.info( "Disk Image install successful" )
-
-
-    result_dir = None
-    if opts.make_iso and not opts.image_only:
-        result_dir = make_livecd( opts.disk_image or disk_img, opts.squashfs_args,
-                                  opts.lorax_templates,
-                                  opts.title, opts.project, opts.releasever,
-                                  opts.volid )
-        # cleanup the mess
-        if disk_img and not opts.keep_image and not opts.disk_image:
-            os.unlink( disk_img )
-            log.info("Disk image erased")
-            disk_img = None
-    elif opts.make_ami and not opts.image_only:
-        result_dir = make_ami(opts.disk_image or disk_img)
-    elif opts.make_appliance and not opts.image_only:
-        if not opts.ks:
-            networks = []
-        else:
-            networks = ks.handler.network.network
-        make_appliance(opts.disk_image or disk_img, opts.app_name,
-                       opts.app_template, opts.app_file, networks, opts.ram,
-                       opts.vcpus, opts.arch, opts.title, opts.project, opts.releasever)
-
-    if opts.result_dir and result_dir:
-        shutil.copytree( result_dir, opts.result_dir )
-        shutil.rmtree( result_dir )
+    if not opts.image_only:
+        result_dir = None
+        if opts.make_iso:
+            work_dir = tempfile.mkdtemp()
+            log.info("working dir is {0}".format(work_dir))
+
+            if (opts.fs_image or opts.no_virt) and not opts.disk_image:
+                # Create iso from a filesystem image
+                disk_img = opts.fs_image or disk_img
+
+                make_squashfs(disk_img, work_dir)
+                with Mount(disk_img, opts="loop") as mount_dir:
+                    result_dir = make_livecd(opts, mount_dir, work_dir)
+            else:
+                # Create iso from a partitioned disk image
+                disk_img = opts.disk_image or disk_img
+                with PartitionMount(disk_img) as img_mount:
+                    if img_mount and img_mount.mount_dir:
+                        make_runtime(opts, img_mount.mount_dir, work_dir)
+                        result_dir = make_livecd(opts, img_mount.mount_dir, work_dir)
+
+            # cleanup the mess
+            # cleanup work_dir?
+            if disk_img and not (opts.keep_image or opts.disk_image or opts.fs_image):
+                os.unlink(disk_img)
+                log.info("Disk image erased")
+                disk_img = None
+        elif opts.make_ami:
+            result_dir = make_ami(opts.disk_image or disk_img)
+        elif opts.make_appliance:
+            if not opts.ks:
+                networks = []
+            else:
+                networks = ks.handler.network.network
+            make_appliance(opts.disk_image or disk_img, opts.app_name,
+                           opts.app_template, opts.app_file, networks, opts.ram,
+                           opts.vcpus, opts.arch, opts.title, opts.project, opts.releasever)
+
+        if opts.result_dir and result_dir:
+            shutil.copytree( result_dir, opts.result_dir )
+            shutil.rmtree( result_dir )
 
     log.info("SUMMARY")
     log.info("-------")
-- 
1.8.1



More information about the anaconda-patches mailing list