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:
parent
894d8b4738
commit
8e84ef10c1
@ -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
|
||||
|
@ -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("-------")
|
||||
|
Loading…
Reference in New Issue
Block a user