From 48c8ae68196c6dffe779a4fd761b1998df90e898 Mon Sep 17 00:00:00 2001 From: "Brian C. Lane" Date: Tue, 19 Feb 2019 16:46:07 -0800 Subject: [PATCH] Add cancel_func to virt and novirt_install functions In addition to monitoring the logs for errors, call a function (or functions) that tell it to cancel the anaconda process and cleanup. Also check for a cancel after creating the squashfs image for live-iso since that's a long running process. This required adding a new argument to a number of existing functions, passing it down to VirtualInstall and novirt_install where the function is called. (cherry picked from commit 4b844756120f516ee33543b884cc52296e603ce7) Resolves: rhbz#1659129 --- src/pylorax/api/queue.py | 2 +- src/pylorax/creator.py | 17 ++++++++++++----- src/pylorax/installer.py | 40 +++++++++++++++++++++++++++------------- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/src/pylorax/api/queue.py b/src/pylorax/api/queue.py index a85535c7..8da45c96 100644 --- a/src/pylorax/api/queue.py +++ b/src/pylorax/api/queue.py @@ -241,7 +241,7 @@ def make_compose(cfg, results_dir): else: open(joinpaths(results_dir, install_cfg.image_name), "w").write("TEST IMAGE") else: - run_creator(install_cfg, callback_func=cancel_build) + run_creator(install_cfg, cancel_func=cancel_build) # Extract the results of the compose into results_dir and cleanup the compose directory move_compose_results(install_cfg, results_dir) diff --git a/src/pylorax/creator.py b/src/pylorax/creator.py index 84333dbf..5c4eb13f 100644 --- a/src/pylorax/creator.py +++ b/src/pylorax/creator.py @@ -405,7 +405,7 @@ def make_squashfs(disk_img, work_dir, compression="xz"): remove(joinpaths(work_dir, "runtime")) -def make_image(opts, ks, callback_func=None): +def make_image(opts, ks, cancel_func=None): """ Install to an image @@ -423,12 +423,12 @@ def make_image(opts, ks, callback_func=None): try: if opts.no_virt: - novirt_install(opts, disk_img, disk_size, ks.handler.method.url, callback_func=callback_func) + novirt_install(opts, disk_img, disk_size, ks.handler.method.url, cancel_func=cancel_func) else: install_log = os.path.abspath(os.path.dirname(opts.logfile))+"/virt-install.log" log.info("install_log = %s", install_log) - virt_install(opts, install_log, disk_img, disk_size) + virt_install(opts, install_log, disk_img, disk_size, cancel_func=cancel_func) except InstallError as e: log.error("Install failed: %s", e) if not opts.keep_image: @@ -489,7 +489,7 @@ def make_live_images(opts, work_dir, root_dir, rootfs_image=None, size=None): return work_dir -def run_creator(opts, callback_func=None): +def run_creator(opts, cancel_func=None): """Run the image creator process :param opts: Commandline options to control the process @@ -537,7 +537,10 @@ def run_creator(opts, callback_func=None): # 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) + disk_img = make_image(opts, ks, cancel_func=cancel_func) + + if cancel_func and cancel_func(): + raise RuntimeError("image creation canceled") # Only create the disk image, return that now if opts.image_only: @@ -552,6 +555,10 @@ def run_creator(opts, callback_func=None): disk_img = opts.fs_image or disk_img make_squashfs(disk_img, work_dir) + + if cancel_func and cancel_func(): + raise RuntimeError("ISO creation canceled") + with Mount(disk_img, opts="loop") as mount_dir: result_dir = make_livecd(opts, mount_dir, work_dir) else: diff --git a/src/pylorax/installer.py b/src/pylorax/installer.py index c903a08b..dab7ad84 100644 --- a/src/pylorax/installer.py +++ b/src/pylorax/installer.py @@ -18,6 +18,7 @@ import logging log = logging.getLogger("pylorax") import os +import glob import shutil import sys import subprocess @@ -116,7 +117,7 @@ class VirtualInstall( object ): """ def __init__( self, iso, ks_paths, disk_img, img_size=2, kernel_args=None, memory=1024, vnc=None, arch=None, - log_check=None, virtio_host="127.0.0.1", virtio_port=6080, + cancel_func=None, virtio_host="127.0.0.1", virtio_port=6080, qcow2=False, boot_uefi=False, ovmf_path=None): """ Start the installation @@ -211,14 +212,14 @@ class VirtualInstall( object ): # TODO: If vnc has been passed, we should look up the port and print that # for the user at this point - while dom.isActive() and not log_check(): + while dom.isActive() and not (cancel_func and cancel_func()): sys.stdout.write(".") sys.stdout.flush() sleep(10) print - if log_check(): - log.info( "Installation error detected. See logfile." ) + if cancel_func and cancel_func(): + log.info( "Installation error or cancel detected. See logfile." ) else: log.info( "Install finished. Or at least virt shut down." ) @@ -234,7 +235,8 @@ class VirtualInstall( object ): # Undefine the virt, UEFI installs need to have --nvram passed subprocess.call(["virsh", "undefine", self.virt_name, "--nvram"]) -def novirt_install(opts, disk_img, disk_size, repo_url, callback_func=None): + +def novirt_install(opts, disk_img, disk_size, repo_url, cancel_func=None): """ Use Anaconda to install to a disk image """ @@ -282,10 +284,14 @@ def novirt_install(opts, disk_img, disk_size, repo_url, callback_func=None): # Create the sparse image mksparse(disk_img, disk_size * 1024**3) + cancel_funcs = [] + if cancel_func is not None: + cancel_funcs.append(cancel_func) + # Make sure anaconda has the right product and release os.environ["ANACONDA_PRODUCTNAME"] = opts.project os.environ["ANACONDA_PRODUCTVERSION"] = opts.releasever - rc = execWithRedirect("anaconda", args, callback_func=callback_func) + rc = execWithRedirect("anaconda", args, callback_func=lambda : any(f() for f in cancel_funcs)) # Move the anaconda logs over to a log directory log_dir = os.path.abspath(os.path.dirname(opts.logfile)) @@ -306,10 +312,13 @@ def novirt_install(opts, disk_img, disk_size, repo_url, callback_func=None): if disk_img: 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)) + + log.debug("Removing device-mapper setup on %s", dm_name) + for d in sorted(glob.glob("/dev/mapper/"+dm_name+"*"), reverse=True): + dm_detach(d) + + log.debug("Removing loop device for %s", disk_img) + loop_detach("/dev/"+get_loop_name(disk_img)) # if selinux_enforcing: # selinux.security_setenforce(1) @@ -342,7 +351,7 @@ def novirt_install(opts, disk_img, disk_size, repo_url, callback_func=None): execWithRedirect("mv", ["-f", qcow2_img, disk_img], raise_err=True) -def virt_install(opts, install_log, disk_img, disk_size): +def virt_install(opts, install_log, disk_img, disk_size, cancel_func=None): """ Use virt-install to install to a disk image @@ -352,6 +361,9 @@ def virt_install(opts, install_log, disk_img, disk_size): """ iso_mount = IsoMountpoint(opts.iso, opts.location) log_monitor = LogMonitor(install_log) + cancel_funcs = [log_monitor.server.log_check] + if cancel_func is not None: + cancel_funcs.append(cancel_func) kernel_args = "" if opts.kernel_args: @@ -375,7 +387,7 @@ def virt_install(opts, install_log, disk_img, disk_size): try: virt = VirtualInstall(iso_mount, opts.ks, diskimg_path, disk_size, kernel_args, opts.ram, opts.vnc, opts.arch, - log_check = log_monitor.server.log_check, + cancel_func = lambda : any(f() for f in cancel_funcs), virtio_host = log_monitor.host, virtio_port = log_monitor.port, qcow2=opts.qcow2, boot_uefi=opts.virt_uefi, @@ -391,7 +403,9 @@ def virt_install(opts, install_log, disk_img, disk_size): iso_mount.umount() if log_monitor.server.log_check(): - raise InstallError("virt_install failed") + raise InstallError("virt_install failed. See logfile.") + elif cancel_func and cancel_func(): + raise InstallError("virt_install canceled by cancel_func") if opts.make_fsimage: mkdiskfsimage(diskimg_path, disk_img, label=opts.fs_label)