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 QEMUInstall and novirt_install where the function is called. Resolves: rhbz#1656691
This commit is contained in:
		
							parent
							
								
									df6d7654bd
								
							
						
					
					
						commit
						850ad9845a
					
				| @ -245,7 +245,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) | ||||
|  | ||||
| @ -456,13 +456,15 @@ def calculate_disk_size(opts, ks): | ||||
|     log.info("Using disk size of %sMiB", disk_size) | ||||
|     return disk_size | ||||
| 
 | ||||
| def make_image(opts, ks): | ||||
| def make_image(opts, ks, cancel_func=None): | ||||
|     """ | ||||
|     Install to a disk image | ||||
| 
 | ||||
|     :param opts: options passed to livemedia-creator | ||||
|     :type opts: argparse options | ||||
|     :param str ks: Path to the kickstart to use for the installation | ||||
|     :param cancel_func: Function that returns True to cancel build | ||||
|     :type cancel_func: function | ||||
|     :returns: Path of the image created | ||||
|     :rtype: str | ||||
| 
 | ||||
| @ -476,12 +478,12 @@ def make_image(opts, ks): | ||||
|     disk_size = calculate_disk_size(opts, ks) | ||||
|     try: | ||||
|         if opts.no_virt: | ||||
|             novirt_install(opts, disk_img, disk_size) | ||||
|             novirt_install(opts, disk_img, disk_size, 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 and os.path.exists(disk_img): | ||||
| @ -576,11 +578,13 @@ def make_live_images(opts, work_dir, disk_img): | ||||
| 
 | ||||
|     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 | ||||
|     :type opts: Either a DataHolder or ArgumentParser | ||||
|     :param cancel_func: Function that returns True to cancel build | ||||
|     :type cancel_func: function | ||||
|     :returns: The result directory and the disk image path. | ||||
|     :rtype: Tuple of str | ||||
| 
 | ||||
| @ -639,7 +643,7 @@ def run_creator(opts, callback_func=None): | ||||
| 
 | ||||
|         # Make the image. Output of this is either a partitioned disk image or a fsimage | ||||
|         try: | ||||
|             disk_img = make_image(opts, ks) | ||||
|             disk_img = make_image(opts, ks, cancel_func=cancel_func) | ||||
|         except InstallError as e: | ||||
|             log.error("ERROR: Image creation failed: %s", e) | ||||
|             raise RuntimeError("Image creation failed: %s" % e) | ||||
| @ -659,6 +663,9 @@ def run_creator(opts, callback_func=None): | ||||
|                 log.error("squashfs.img creation failed") | ||||
|                 raise RuntimeError("squashfs.img creation failed") | ||||
| 
 | ||||
|             if 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: | ||||
|  | ||||
| @ -146,7 +146,7 @@ class QEMUInstall(object): | ||||
| 
 | ||||
|     def __init__(self, opts, iso, ks_paths, disk_img, img_size=2048, | ||||
|                  kernel_args=None, memory=1024, vcpus=None, 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, | ||||
|                  image_type=None, boot_uefi=False, ovmf_path=None): | ||||
|         """ | ||||
|         Start the installation | ||||
| @ -162,8 +162,8 @@ class QEMUInstall(object): | ||||
|         :param int vcpus: Number of virtual cpus | ||||
|         :param str vnc: Arguments to pass to qemu -display | ||||
|         :param str arch: Optional architecture to use in the virt | ||||
|         :param log_check: Method that returns True if the installation fails | ||||
|         :type log_check: method | ||||
|         :param cancel_func: Function that returns True if the installation fails | ||||
|         :type cancel_func: function | ||||
|         :param str virtio_host: Hostname to connect virtio log to | ||||
|         :param int virtio_port: Port to connect virtio log to | ||||
|         :param str image_type: Type of qemu-img disk to create, or None. | ||||
| @ -249,7 +249,7 @@ class QEMUInstall(object): | ||||
|         log.debug(qemu_cmd) | ||||
|         try: | ||||
|             execWithRedirect(qemu_cmd[0], qemu_cmd[1:], reset_lang=False, raise_err=True, | ||||
|                              callback=lambda p: not log_check()) | ||||
|                              callback=lambda p: not cancel_func()) | ||||
|         except subprocess.CalledProcessError as e: | ||||
|             log.error("Running qemu failed:") | ||||
|             log.error("cmd: %s", " ".join(e.cmd)) | ||||
| @ -263,27 +263,30 @@ class QEMUInstall(object): | ||||
|             if boot_uefi and ovmf_path: | ||||
|                 os.unlink(ovmf_vars) | ||||
| 
 | ||||
|         if log_check(): | ||||
|         if cancel_func(): | ||||
|             log.error("Installation error detected. See logfile for details.") | ||||
|             raise InstallError("QEMUInstall failed") | ||||
|         else: | ||||
|             log.info("Installation finished without errors.") | ||||
| 
 | ||||
| 
 | ||||
| def novirt_log_check(log_check, proc): | ||||
| def novirt_cancel_check(cancel_funcs, proc): | ||||
|     """ | ||||
|     Check to see if there has been an error in the logs | ||||
| 
 | ||||
|     :param log_check: method to call to check for an error in the logs | ||||
|     :param cancel_funcs: list of functions to call, True from any one cancels the build | ||||
|     :type cancel_funcs: list | ||||
|     :param proc: Popen object for the anaconda process | ||||
|     :type proc: subprocess.Popen | ||||
|     :returns: True if the process has been terminated | ||||
| 
 | ||||
|     The log_check method should return a True if an error has been detected. | ||||
|     The cancel_funcs functions should return a True if an error has been detected. | ||||
|     When an error is detected the process is terminated and this returns True | ||||
|     """ | ||||
|     if log_check(): | ||||
|         proc.terminate() | ||||
|         return True | ||||
|     for f in cancel_funcs: | ||||
|         if f(): | ||||
|             proc.terminate() | ||||
|             return True | ||||
|     return False | ||||
| 
 | ||||
| 
 | ||||
| @ -314,7 +317,7 @@ def anaconda_cleanup(dirinstall_path): | ||||
|     return rc | ||||
| 
 | ||||
| 
 | ||||
| def novirt_install(opts, disk_img, disk_size): | ||||
| def novirt_install(opts, disk_img, disk_size, cancel_func=None): | ||||
|     """ | ||||
|     Use Anaconda to install to a disk image | ||||
| 
 | ||||
| @ -322,6 +325,8 @@ def novirt_install(opts, disk_img, disk_size): | ||||
|     :type opts: argparse options | ||||
|     :param str disk_img: The full path to the disk image to be created | ||||
|     :param int disk_size: The size of the disk_img in MiB | ||||
|     :param cancel_func: Function that returns True to cancel build | ||||
|     :type cancel_func: function | ||||
| 
 | ||||
|     This method runs anaconda to create the image and then based on the opts | ||||
|     passed creates a qemu disk image or tarfile. | ||||
| @ -371,6 +376,9 @@ def novirt_install(opts, disk_img, disk_size): | ||||
| 
 | ||||
|     log_monitor = LogMonitor(timeout=opts.timeout) | ||||
|     args += ["--remotelog", "%s:%s" % (log_monitor.host, log_monitor.port)] | ||||
|     cancel_funcs = [log_monitor.server.log_check] | ||||
|     if cancel_func is not None: | ||||
|         cancel_funcs.append(cancel_func) | ||||
| 
 | ||||
|     # Make sure anaconda has the right product and release | ||||
|     log.info("Running anaconda.") | ||||
| @ -378,7 +386,7 @@ def novirt_install(opts, disk_img, disk_size): | ||||
|         for line in execReadlines("anaconda", args, reset_lang=False, | ||||
|                                   env_add={"ANACONDA_PRODUCTNAME": opts.project, | ||||
|                                            "ANACONDA_PRODUCTVERSION": opts.releasever}, | ||||
|                                   callback=lambda p: not novirt_log_check(log_monitor.server.log_check, p)): | ||||
|                                   callback=lambda p: not novirt_cancel_check(cancel_funcs, p)): | ||||
|             log.info(line) | ||||
| 
 | ||||
|         # Make sure the new filesystem is correctly labeled | ||||
| @ -424,10 +432,14 @@ def novirt_install(opts, disk_img, disk_size): | ||||
| 
 | ||||
|         if not opts.make_iso and not opts.make_fsimage and not opts.make_pxe_live: | ||||
|             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)) | ||||
| 
 | ||||
|             # Remove device-mapper for partitions and disk | ||||
|             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)) | ||||
| 
 | ||||
|     # qemu disk image is used by bare qcow2 images and by Vagrant | ||||
|     if opts.image_type: | ||||
| @ -493,7 +505,7 @@ def novirt_install(opts, disk_img, disk_size): | ||||
|         execWithRedirect("fallocate", ["--dig-holes", 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 qemu to install to a disk image | ||||
| 
 | ||||
| @ -502,6 +514,8 @@ def virt_install(opts, install_log, disk_img, disk_size): | ||||
|     :param str install_log: The path to write the log from qemu | ||||
|     :param str disk_img: The full path to the disk image to be created | ||||
|     :param int disk_size: The size of the disk_img in MiB | ||||
|     :param cancel_func: Function that returns True to cancel build | ||||
|     :type cancel_func: function | ||||
| 
 | ||||
|     This uses qemu with a boot.iso and a kickstart to create a disk | ||||
|     image and then optionally, based on the opts passed, creates tarfile. | ||||
| @ -512,6 +526,9 @@ def virt_install(opts, install_log, disk_img, disk_size): | ||||
|         raise InstallError("ISO is missing stage2, cannot continue") | ||||
| 
 | ||||
|     log_monitor = LogMonitor(install_log, timeout=opts.timeout) | ||||
|     cancel_funcs = [log_monitor.server.log_check] | ||||
|     if cancel_func is not None: | ||||
|         cancel_funcs.append(cancel_func) | ||||
| 
 | ||||
|     kernel_args = "" | ||||
|     if opts.kernel_args: | ||||
| @ -536,7 +553,7 @@ def virt_install(opts, install_log, disk_img, disk_size): | ||||
|     try: | ||||
|         QEMUInstall(opts, iso_mount, opts.ks, diskimg_path, disk_size, | ||||
|                     kernel_args, opts.ram, opts.vcpus, 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, | ||||
|                     image_type=opts.image_type, boot_uefi=opts.virt_uefi, | ||||
| @ -555,6 +572,8 @@ def virt_install(opts, install_log, disk_img, disk_size): | ||||
|         else: | ||||
|             msg = "virt_install failed on line: %s" % log_monitor.server.error_line | ||||
|         raise InstallError(msg) | ||||
|     elif cancel_func(): | ||||
|         raise InstallError("virt_install canceled by cancel_func") | ||||
| 
 | ||||
|     if opts.make_fsimage: | ||||
|         mkfsimage_from_disk(diskimg_path, disk_img, disk_size, label=opts.fs_label) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user