lorax-composer: Cancel running Anaconda process

It ends up that this isn't as easy as you'd think. Anaconda sets up some
signal handlers to handle cleanly exiting, but they are not being run
when sent a TERM after package installation has started. I think DNF
resets them causing it to get ignored.

When the cancel is sent it can take several minutes for it to have an
effect. In my testing it usually takes around 2 minutes for anaconda to
notice and exit.

This sends a TERM to the process and then waits for it to exit. When it
returns it then removed any device-mapper devices that were setup for
image installations, removes any hanging loop devices.

It then kills off any process with pyanaconda. in the cmdline, and
anaconda-bus.conf (because anaconda starts a bunch of helpers and if it
doesn't shut down cleanly they remain running).

Resolves: rhbz#1656691
This commit is contained in:
Brian C. Lane 2018-12-12 16:44:05 -08:00
parent 850ad9845a
commit 42addfc2b5

View File

@ -21,9 +21,9 @@ import glob
import json import json
from math import ceil from math import ceil
import os import os
import subprocess
import shutil import shutil
import socket import socket
import subprocess
import tempfile import tempfile
# Use the Lorax treebuilder branch for iso creation # Use the Lorax treebuilder branch for iso creation
@ -285,7 +285,10 @@ def novirt_cancel_check(cancel_funcs, proc):
""" """
for f in cancel_funcs: for f in cancel_funcs:
if f(): if f():
log.info("Terminating process %d", proc.pid)
proc.terminate() proc.terminate()
# NOTE: Have to return and allow execReadlines to call proc.communicate()
return True return True
return False return False
@ -386,6 +389,7 @@ def novirt_install(opts, disk_img, disk_size, cancel_func=None):
for line in execReadlines("anaconda", args, reset_lang=False, for line in execReadlines("anaconda", args, reset_lang=False,
env_add={"ANACONDA_PRODUCTNAME": opts.project, env_add={"ANACONDA_PRODUCTNAME": opts.project,
"ANACONDA_PRODUCTVERSION": opts.releasever}, "ANACONDA_PRODUCTVERSION": opts.releasever},
reset_handlers=False,
callback=lambda p: not novirt_cancel_check(cancel_funcs, p)): callback=lambda p: not novirt_cancel_check(cancel_funcs, p)):
log.info(line) log.info(line)
@ -441,6 +445,12 @@ def novirt_install(opts, disk_img, disk_size, cancel_func=None):
log.debug("Removing loop device for %s", disk_img) log.debug("Removing loop device for %s", disk_img)
loop_detach("/dev/"+get_loop_name(disk_img)) loop_detach("/dev/"+get_loop_name(disk_img))
# When anaconda crashes or is canceled it leaves pyanaconda.* running
execWithRedirect("pkill", ["-f", "pyanaconda."])
# It can also leave dbus running
execWithRedirect("pkill", ["-f", "anaconda-bus.conf"])
# qemu disk image is used by bare qcow2 images and by Vagrant # qemu disk image is used by bare qcow2 images and by Vagrant
if opts.image_type: if opts.image_type:
log.info("Converting %s to %s", disk_img, opts.image_type) log.info("Converting %s to %s", disk_img, opts.image_type)