Add log monitoring to lmc --no-virt installation

Previously if there was an error during a novirt installation that
didn't exit the process there was no way to detect it. This uses the new
--remotelog option for anaconda to monitor the logs for errors using the
same criteria as it does when monitoring a virt install. If there is an
error the anaconda process will be terminated and the logs will be
gathered up into ./anaconda/
This commit is contained in:
Brian C. Lane 2015-01-09 13:13:55 -08:00
parent bd501cccef
commit d96ed99621
2 changed files with 43 additions and 7 deletions

View File

@ -23,6 +23,7 @@
import os, sys
import subprocess
import threading
from time import sleep
import logging
log = logging.getLogger("pylorax")
@ -66,7 +67,7 @@ class tee(threading.Thread):
def execWithRedirect(command, argv, stdin = None, stdout = None,
stderr = None, root = None, preexec_fn=None, cwd=None,
raise_err=False):
raise_err=False, callback=None):
""" Run an external program and redirect the output to a file.
@param command The command to run.
@param argv A list of arguments.
@ -77,7 +78,11 @@ def execWithRedirect(command, argv, stdin = None, stdout = None,
@param preexec_fn function to pass to Popen
@param cwd working directory to pass to Popen
@param raise_err raise CalledProcessError when the returncode is not 0
@param callback method to call while waiting for process to exit.
@return The return code of command.
The callback is passed the Popen object. It should return False if
the polling loop should be exited.
"""
def chroot ():
os.chroot(root)
@ -143,6 +148,9 @@ def execWithRedirect(command, argv, stdin = None, stdout = None,
preexec_fn=preexec_fn, cwd=cwd,
env=env)
if callback:
while callback(proc) and proc.poll() is None:
sleep(1)
proc.wait()
ret = proc.returncode

View File

@ -88,7 +88,7 @@ class LogRequestHandler(SocketServer.BaseRequestHandler):
if self.server.log_path:
self.fp = open(self.server.log_path, "w") # pylint: disable=attribute-defined-outside-init
else:
print "no log_path specified"
self.fp = None
self.request.settimeout(10)
def handle(self):
@ -107,8 +107,9 @@ class LogRequestHandler(SocketServer.BaseRequestHandler):
try:
data = self.request.recv(4096)
self.fp.write(data)
self.fp.flush()
if self.fp:
self.fp.write(data)
self.fp.flush()
# check the data for errors and set error flag
# need to assemble it into lines so we can test for the error
@ -130,7 +131,8 @@ class LogRequestHandler(SocketServer.BaseRequestHandler):
def finish(self):
self.request.close()
self.fp.close()
if self.fp:
self.fp.close()
def iserror(self, line):
"""
@ -193,7 +195,7 @@ class LogMonitor(object):
This needs to be running before the virt-install runs, it expects
there to be a listener on the port used for the virtio log port.
"""
def __init__(self, log_path, host="localhost", port=0):
def __init__(self, log_path=None, host="localhost", port=0):
"""
Start a thread to monitor the logs.
@ -203,6 +205,9 @@ class LogMonitor(object):
If 0 is passed for the port the dynamically assigned port will be
available as self.port
If log_path isn't set then it only monitors the logs, instead of
also writing them to disk.
"""
self.server = LogServer(log_path, (host, port), LogRequestHandler)
self.host, self.port = self.server.server_address
@ -590,6 +595,23 @@ def make_livecd(opts, mount_dir, work_dir):
return work_dir
def novirt_log_check(log_check, 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 proc: Popen object for the anaconda process
:returns: True if the process has been terminated
The log_check method 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
return False
def novirt_install(opts, disk_img, disk_size, repo_url):
"""
Use Anaconda to install to a disk image
@ -648,12 +670,16 @@ def novirt_install(opts, disk_img, disk_size, repo_url):
# Create the sparse image
mksparse(disk_img, disk_size * 1024**2)
log_monitor = LogMonitor()
args += ["--remotelog", "%s:%s" % (log_monitor.host, log_monitor.port)]
# Make sure anaconda has the right product and release
os.environ["ANACONDA_PRODUCTNAME"] = opts.project
os.environ["ANACONDA_PRODUCTVERSION"] = opts.releasever
log.info("Running anaconda.")
try:
execWithRedirect("anaconda", args, raise_err=True)
execWithRedirect("anaconda", args, raise_err=True,
callback=lambda p: not novirt_log_check(log_monitor.server.log_check, p))
# Make sure the new filesystem is correctly labeled
args = ["-e", "/proc", "-e", "/sys", "-e", "/dev",
@ -668,6 +694,8 @@ def novirt_install(opts, disk_img, disk_size, repo_url):
log.error("Running anaconda failed: %s", e)
raise InstallError("novirt_install failed")
finally:
log_monitor.shutdown()
# 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")