Move core of livemedia-creator to run_creator()

We need to be able to share the output types from livemedia-creator with
lorax-composer, so move the core of the main() function into
run_creatoe(). Pass in the cmdline args or a DataHolder with them set.
This commit is contained in:
Brian C. Lane 2018-02-21 15:17:08 -08:00
parent 85ee784efc
commit 59fd3934f3
2 changed files with 153 additions and 127 deletions

View File

@ -28,13 +28,17 @@ import glob
from mako.template import Template
from mako.exceptions import text_error_template
# Use pykickstart to calculate disk image size
from pykickstart.parser import KickstartParser
from pykickstart.version import makeVersion, RHEL7
# Use the Lorax treebuilder branch for iso creation
from pylorax import ArchData
from pylorax.base import DataHolder
from pylorax.treebuilder import TreeBuilder, RuntimeBuilder
from pylorax.treebuilder import findkernels
from pylorax.sysutils import joinpaths, remove
from pylorax.imgutils import mount, umount
from pylorax.imgutils import Mount, PartitionMount, copytree, mount, umount
from pylorax.imgutils import mksquashfs, mkrootfsimg
from pylorax.executils import execWithRedirect, runcmd
from pylorax.installer import InstallError, novirt_install, virt_install
@ -46,6 +50,17 @@ DRACUT_DEFAULT = ["--xz", "--add", "livenet dmsquash-live convertfs pollcdrom",
"--omit", "plymouth", "--no-hostonly", "--no-early-microcode"]
def get_ks_disk_size(ks):
"""Return the size of the kickstart's disk partitions
:param ks: The kickstart
:type ks: Kickstart object
:returns: The size of the disk, in GiB
"""
disk_size = 1 + (sum([p.size for p in ks.handler.partition.partitions]) / 1024)
log.info("disk_size = %sGiB", disk_size)
return disk_size
def is_image_mounted(disk_img):
"""
Return True if the disk_img is mounted
@ -390,7 +405,7 @@ def make_squashfs(disk_img, work_dir, compression="xz"):
remove(joinpaths(work_dir, "runtime"))
def make_image(opts, ks):
def make_image(opts, ks, callback_func=None):
"""
Install to an image
@ -398,8 +413,7 @@ def make_image(opts, ks):
Returns the full path of of the image created.
"""
disk_size = 1 + (sum([p.size for p in ks.handler.partition.partitions]) / 1024)
log.info("disk_size = %sGB", disk_size)
disk_size = get_ks_disk_size(ks)
if opts.image_name:
disk_img = joinpaths(opts.result_dir, opts.image_name)
@ -409,7 +423,7 @@ def make_image(opts, ks):
try:
if opts.no_virt:
novirt_install(opts, disk_img, disk_size, ks.handler.method.url)
novirt_install(opts, disk_img, disk_size, ks.handler.method.url, callback_func=callback_func)
else:
install_log = os.path.abspath(os.path.dirname(opts.logfile))+"/virt-install.log"
log.info("install_log = %s", install_log)
@ -473,3 +487,127 @@ def make_live_images(opts, work_dir, root_dir, rootfs_image=None, size=None):
create_pxe_config(template, work_dir, live_image_name, add_pxe_args)
return work_dir
def run_creator(opts, callback_func=None):
"""Run the image creator process
:param opts: Commandline options to control the process
:type opts: Either a DataHolder or ArgumentParser
:returns: The result directory and the disk image path.
:rtype: Tuple of str
This function takes the opts arguments and creates the selected output image.
See the cmdline --help for livemedia-creator for the possible options
(Yes, this is not ideal, but we can fix that later)
"""
result_dir = None
# Parse the kickstart
if opts.ks:
ks_version = makeVersion(RHEL7)
ks = KickstartParser( ks_version, errorsAreFatal=False, missingIncludeIsFatal=False )
ks.readKickstart( opts.ks[0] )
# Make the disk or filesystem image
if not opts.disk_image and not opts.fs_image:
if not opts.ks:
raise RuntimeError("Image creation requires a kickstart file")
errors = []
if ks.handler.method.method != "url" and opts.no_virt:
errors.append("Only url install method is currently supported. Please "
"fix your kickstart file." )
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.")
if opts.make_fsimage:
# Make sure the kickstart isn't using autopart and only has a / mountpoint
part_ok = not any(p for p in ks.handler.partition.partitions
if p.mountpoint not in ["/", "swap"])
if not part_ok or ks.handler.autopart.seen:
errors.append("Filesystem images must use a single / part, not autopart or "
"multiple partitions. swap is allowed but not used.")
if errors:
raise RuntimeError("\n".join(errors))
# Make the image. Output of this is either a partitioned disk image or a fsimage
# Can also fail with InstallError
disk_img = make_image(opts, ks, callback_func=callback_func)
# Only create the disk image, return that now
if opts.image_only:
return (result_dir, disk_img)
if opts.make_iso:
work_dir = tempfile.mkdtemp()
log.info("working dir is %s", 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_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)
elif opts.make_pxe_live:
work_dir = tempfile.mkdtemp()
log.info("working dir is %s", work_dir)
if (opts.fs_image or opts.no_virt) and not opts.disk_image:
# Create pxe live images from a filesystem image
disk_img = opts.fs_image or disk_img
with Mount(disk_img, opts="loop") as mnt_dir:
result_dir = make_live_images(opts, work_dir, mnt_dir, rootfs_image=disk_img)
else:
# Create pxe live images from a partitioned disk image
disk_img = opts.disk_image or disk_img
is_root_part = None
if opts.ostree:
is_root_part = lambda dir: os.path.exists(dir+"/ostree/deploy")
with PartitionMount(disk_img, mount_ok=is_root_part) as img_mount:
if img_mount and img_mount.mount_dir:
try:
mounted_sysroot_boot_dir = None
if opts.ostree:
mounted_sysroot_boot_dir = mount_boot_part_over_root(img_mount)
if opts.live_rootfs_keep_size:
size = img_mount.mount_size / 1024**3
else:
size = opts.live_rootfs_size or None
result_dir = make_live_images(opts, work_dir, img_mount.mount_dir, size=size)
finally:
if mounted_sysroot_boot_dir:
umount(mounted_sysroot_boot_dir)
if opts.result_dir != opts.tmp and result_dir:
copytree(result_dir, opts.result_dir, preserve=False)
shutil.rmtree( result_dir )
result_dir = None
return (result_dir, disk_img)

View File

@ -24,21 +24,12 @@ pylorax_log = logging.getLogger("pylorax")
import argparse
import os
import shutil
import sys
import tempfile
# Use pykickstart to calculate disk image size
from pykickstart.parser import KickstartParser
from pykickstart.version import makeVersion, RHEL7
from pylorax import vernum
from pylorax.creator import DRACUT_DEFAULT, mount_boot_part_over_root
from pylorax.creator import make_appliance, make_image, make_livecd, make_live_images
from pylorax.creator import make_runtime, make_squashfs
from pylorax.imgutils import copytree, default_image_name
from pylorax.imgutils import Mount, PartitionMount, umount
from pylorax.installer import InstallError
from pylorax.creator import DRACUT_DEFAULT, run_creator
from pylorax.imgutils import default_image_name
from pylorax.sysutils import joinpaths
VERSION = "{0}-{1}".format(os.path.basename(sys.argv[0]), vernum)
@ -246,7 +237,7 @@ def main():
sys.exit( 1 )
if opts.result_dir and os.path.exists(opts.result_dir):
log.error( "The results_dir (%s) should not exist, please delete or "
log.error( "The result_dir (%s) should not exist, please delete or "
"move its contents", opts.result_dir)
sys.exit( 1 )
@ -347,115 +338,12 @@ def main():
tempfile.tempdir = opts.tmp
disk_img = None
# Parse the kickstart
if opts.ks:
ks_version = makeVersion(RHEL7)
ks = KickstartParser( ks_version, errorsAreFatal=False, missingIncludeIsFatal=False )
ks.readKickstart( opts.ks[0] )
# 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)
errors = []
if ks.handler.method.method != "url" and opts.no_virt:
errors.append("Only url install method is currently supported. Please "
"fix your kickstart file." )
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.")
if opts.make_fsimage:
# Make sure the kickstart isn't using autopart and only has a / mountpoint
part_ok = not any(p for p in ks.handler.partition.partitions
if p.mountpoint not in ["/", "swap"])
if not part_ok or ks.handler.autopart.seen:
errors.append("Filesystem images must use a single / part, not autopart or "
"multiple partitions. swap is allowed but not used.")
if errors:
for _e in errors:
log.error(_e)
sys.exit(1)
# 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)
if not opts.image_only:
result_dir = None
if opts.make_iso:
work_dir = tempfile.mkdtemp()
log.info("working dir is %s", 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_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)
elif opts.make_pxe_live:
work_dir = tempfile.mkdtemp()
log.info("working dir is %s", work_dir)
if (opts.fs_image or opts.no_virt) and not opts.disk_image:
# Create pxe live images from a filesystem image
disk_img = opts.fs_image or disk_img
with Mount(disk_img, opts="loop") as mnt_dir:
result_dir = make_live_images(opts, work_dir, mnt_dir, rootfs_image=disk_img)
else:
# Create pxe live images from a partitioned disk image
disk_img = opts.disk_image or disk_img
is_root_part = None
if opts.ostree:
is_root_part = lambda dir: os.path.exists(dir+"/ostree/deploy")
with PartitionMount(disk_img, mount_ok=is_root_part) as img_mount:
if img_mount and img_mount.mount_dir:
try:
mounted_sysroot_boot_dir = None
if opts.ostree:
mounted_sysroot_boot_dir = mount_boot_part_over_root(img_mount)
if opts.live_rootfs_keep_size:
size = img_mount.mount_size / 1024**3
else:
size = opts.live_rootfs_size or None
result_dir = make_live_images(opts, work_dir, img_mount.mount_dir, size=size)
finally:
if mounted_sysroot_boot_dir:
umount(mounted_sysroot_boot_dir)
if opts.result_dir != opts.tmp and result_dir:
copytree(result_dir, opts.result_dir, preserve=False)
shutil.rmtree( result_dir )
try:
# TODO - Better API than passing in opts
(result_dir, disk_img) = run_creator(opts)
except Exception as e:
log.error(str(e))
sys.exit(1)
log.info("SUMMARY")
log.info("-------")
@ -464,7 +352,7 @@ def main():
log.info("Disk image is at %s", disk_img)
if opts.make_appliance:
log.info("Appliance description is in %s", opts.app_file)
log.info("Results are in %s", opts.result_dir or result_dir)
log.info("Results are in %s", result_dir or opts.result_dir)
sys.exit( 0 )