Add filesystem image install support

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.
This commit is contained in:
Brian C. Lane 2013-01-30 17:16:25 -05:00
parent 894d8b4738
commit 8e84ef10c1
2 changed files with 372 additions and 265 deletions

View File

@ -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.
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
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.
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

View File

@ -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 ):
"""
@ -252,55 +253,55 @@ class VirtualInstall( object ):
self.virt_name = "LiveOS-"+str(uuid.uuid4())
# add --graphics none later
# add whatever serial cmds are needed later
cmd = [ "virt-install", "-n", self.virt_name,
"-r", str(memory),
"--noreboot",
"--noautoconsole" ]
args = ["-n", self.virt_name,
"-r", str(memory),
"--noreboot",
"--noautoconsole"]
cmd.append("--graphics")
args.append("--graphics")
if vnc:
cmd.append(vnc)
args.append(vnc)
else:
cmd.append("none")
args.append("none")
for ks in ks_paths:
cmd.append("--initrd-inject")
cmd.append(ks)
args.append("--initrd-inject")
args.append(ks)
disk_opts = "path={0}".format(disk_img)
disk_opts += ",format=raw"
if not os.path.isfile(disk_img):
disk_opts += ",size={0}".format(img_size)
cmd.append("--disk")
cmd.append(disk_opts)
args.append("--disk")
args.append(disk_opts)
if iso.liveos:
disk_opts = "path={0},device=cdrom".format(iso.iso_path)
cmd.append("--disk")
cmd.append(disk_opts)
args.append("--disk")
args.append(disk_opts)
extra_args = "ks=file:/{0}".format(os.path.basename(ks_paths[0]))
if kernel_args:
extra_args += " "+kernel_args
if iso.liveos:
extra_args += " stage2=live:CDLABEL={0}".format(udev_escape(iso.label))
cmd.append("--extra-args")
cmd.append(extra_args)
args.append("--extra-args")
args.append(extra_args)
cmd.append("--location")
cmd.append(iso.mount_dir)
args.append("--location")
args.append(iso.mount_dir)
channel_args = "tcp,host={0}:{1},mode=connect,target_type=virtio" \
",name=org.fedoraproject.anaconda.log.0".format(
virtio_host, virtio_port)
cmd.append("--channel")
cmd.append(channel_args)
args.append("--channel")
args.append(channel_args)
if arch:
cmd.append("--arch")
cmd.append(arch)
args.append("--arch")
args.append(arch)
rc = execWithRedirect( cmd[0], cmd[1:] )
rc = execWithRedirect("virt-install", args)
if rc:
raise Exception("Problem starting virtual install")
@ -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
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))
# 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)
def get_kernels(self):
"""
Get a list of the kernels in the boot_dir
# Create the sparse image
mksparse( disk_img, disk_size * 1024**3 )
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-")]
cmd = [ "anaconda", "--image", disk_img, "--kickstart", kickstart,
"--cmdline", "--repo", repo_url ]
cmd += args
def get_kernel_arch(self):
"""
Get the arch of the first kernel in boot_dir
rc = execWithRedirect( cmd[0], cmd[1:] )
if selinux_enforcing:
selinux.security_setenforce(1)
return rc
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" ))
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]
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()
return work_dir
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)
args = ["--kickstart", opts.ks[0], "--cmdline", "--repo", repo_url]
if opts.anaconda_args:
for arg in opts.anaconda_args:
args += arg.split(" ", 1)
if opts.proxy:
args += ["--proxy", opts.proxy]
if opts.armplatform:
args += ["--armplatform", opts.armplatform]
if opts.make_iso or opts.make_fsimage:
# Make a blank fs image
args += ["--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:
args += ["--image", disk_img]
# Create the sparse image
mksparse(disk_img, disk_size * 1024**3)
rc = execWithRedirect("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 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:
kernel_arch = "i386"
log.debug( "kernel_arch = {0}".format(kernel_arch) )
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
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
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()
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
errors.append("Only url install method is currently supported. Please "
"fix your kickstart file." )
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.")
errors.append("The kickstart must not set a display mode (text, cmdline, "
"graphical), this will interfere with livemedia-creator.")
if errors:
for e in errors:
log.error(e)
sys.exit(1)
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"
# 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)
log.info( "disk_img = {0}".format(disk_img) )
log.info( "install_log = {0}".format(install_log) )
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.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 ]
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
# Use anaconda's image install
install_error = anaconda_install( disk_img, disk_size, opts.ks[0],
repo_url, anaconda_args )
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)
# 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 )
# 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 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))
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 opts.result_dir and result_dir:
shutil.copytree( result_dir, opts.result_dir )
shutil.rmtree( result_dir )
log.info("SUMMARY")
log.info("-------")