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:
parent
bd501cccef
commit
d96ed99621
@ -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
|
||||
|
||||
|
@ -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,6 +107,7 @@ class LogRequestHandler(SocketServer.BaseRequestHandler):
|
||||
|
||||
try:
|
||||
data = self.request.recv(4096)
|
||||
if self.fp:
|
||||
self.fp.write(data)
|
||||
self.fp.flush()
|
||||
|
||||
@ -130,6 +131,7 @@ class LogRequestHandler(SocketServer.BaseRequestHandler):
|
||||
|
||||
def finish(self):
|
||||
self.request.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")
|
||||
|
Loading…
Reference in New Issue
Block a user