[PATCH] Add log monitoring to lmc --novirt installation
Brian C. Lane
bcl at redhat.com
Fri Jan 9 22:59:55 UTC 2015
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/
---
src/pylorax/executils.py | 10 +++++++++-
src/sbin/livemedia-creator | 40 ++++++++++++++++++++++++++++++++++------
2 files changed, 43 insertions(+), 7 deletions(-)
diff --git a/src/pylorax/executils.py b/src/pylorax/executils.py
index 0b261c3..6fbb271 100644
--- a/src/pylorax/executils.py
+++ b/src/pylorax/executils.py
@@ -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
diff --git a/src/sbin/livemedia-creator b/src/sbin/livemedia-creator
index 1472f21..b21cdbe 100755
--- a/src/sbin/livemedia-creator
+++ b/src/sbin/livemedia-creator
@@ -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")
--
1.9.3
More information about the anaconda-patches
mailing list