Additional comments and a file helper class.
Improve the comments in the forking file as well as unify the forking related filenames under a helper class that also provides the converted pid as well as other util methods used in the main forking class.
This commit is contained in:
@@ -21,13 +21,12 @@ from anvil import log as logging
|
||||
from anvil import runners as base
|
||||
from anvil import shell as sh
|
||||
from anvil import trace as tr
|
||||
from anvil import utils
|
||||
|
||||
from anvil.components import (STATUS_STARTED, STATUS_UNKNOWN)
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Trace constants
|
||||
PID_FN = "PID_FN"
|
||||
STDOUT_FN = "STDOUT_FN"
|
||||
STDERR_FN = "STDERR_FN"
|
||||
@@ -36,93 +35,136 @@ NAME = "NAME"
|
||||
FORK_TEMPL = "%s.fork"
|
||||
|
||||
|
||||
class ForkRunner(base.Runner):
|
||||
def stop(self, app_name):
|
||||
trace_dir = self.runtime.get_option('trace_dir')
|
||||
if not sh.isdir(trace_dir):
|
||||
msg = "No trace directory found from which to stop: %s" % (app_name)
|
||||
raise excp.StopException(msg)
|
||||
with sh.Rooted(True):
|
||||
fn_name = FORK_TEMPL % (app_name)
|
||||
(pid_file, stderr_fn, stdout_fn) = self._form_file_names(fn_name)
|
||||
pid = self._extract_pid(pid_file)
|
||||
if not pid:
|
||||
msg = "Could not extract a valid pid from %s" % (pid_file)
|
||||
raise excp.StopException(msg)
|
||||
(killed, attempts) = sh.kill(pid)
|
||||
# Trash the files if it worked
|
||||
if killed:
|
||||
LOG.debug("Killed pid %s after %s attempts." % (pid, attempts))
|
||||
LOG.debug("Removing pid file %s" % (pid_file))
|
||||
sh.unlink(pid_file)
|
||||
LOG.debug("Removing stderr file %r" % (stderr_fn))
|
||||
sh.unlink(stderr_fn)
|
||||
LOG.debug("Removing stdout file %r" % (stdout_fn))
|
||||
sh.unlink(stdout_fn)
|
||||
trace_fn = tr.trace_filename(trace_dir, fn_name)
|
||||
if sh.isfile(trace_fn):
|
||||
LOG.debug("Removing %r trace file %r" % (app_name, trace_fn))
|
||||
sh.unlink(trace_fn)
|
||||
else:
|
||||
msg = "Could not stop %r after %s attempts" % (app_name, attempts)
|
||||
raise excp.StopException(msg)
|
||||
class ForkFiles(object):
|
||||
def __init__(self, pid, stdout, stderr, trace=None):
|
||||
self.pid = pid
|
||||
self.stdout = stdout
|
||||
self.stderr = stderr
|
||||
self.trace = trace
|
||||
|
||||
def _extract_pid(self, filename):
|
||||
if sh.isfile(filename):
|
||||
def extract_pid(self):
|
||||
# Load the pid file and take out the pid from it...
|
||||
#
|
||||
# Typically said file has a integer pid in it so load said file
|
||||
# and covert its contents to an int or fail trying...
|
||||
if self.pid and sh.isfile(self.pid):
|
||||
try:
|
||||
return int(sh.load_file(filename).strip())
|
||||
except ValueError:
|
||||
return int(sh.load_file(self.pid).strip())
|
||||
except (ValueError, TypeError, IOError):
|
||||
return None
|
||||
else:
|
||||
return None
|
||||
|
||||
def as_list(self):
|
||||
possibles = [self.pid, self.stdout, self.stderr, self.trace]
|
||||
return [i for i in possibles if i is not None]
|
||||
|
||||
def as_dict(self):
|
||||
return {
|
||||
PID_FN: self.pid,
|
||||
STDOUT_FN: self.stdout,
|
||||
STDERR_FN: self.stderr,
|
||||
}
|
||||
|
||||
|
||||
class ForkRunner(base.Runner):
|
||||
def stop(self, app_name):
|
||||
# The location of the pid file should be in the attached
|
||||
# runtimes trace directory, so see if we can find said file
|
||||
# and then attempt to kill the pid that exists in that file
|
||||
# which if succesffully will signal to the rest of this code
|
||||
# that we can go through and cleanup the other remnants of said
|
||||
# pid such as the stderr/stdout files that were being written to...
|
||||
trace_dir = self.runtime.get_option('trace_dir')
|
||||
if not sh.isdir(trace_dir):
|
||||
msg = "No trace directory found from which to stop: %r" % (app_name)
|
||||
raise excp.StopException(msg)
|
||||
with sh.Rooted(True):
|
||||
fork_fns = self._form_file_names(app_name)
|
||||
pid = fork_fns.extract_pid()
|
||||
if pid is None:
|
||||
msg = "Could not extract a valid pid from %r" % (fork_fns.pid)
|
||||
raise excp.StopException(msg)
|
||||
(killed, attempts) = sh.kill(pid)
|
||||
# Trash the files if it worked
|
||||
if killed:
|
||||
LOG.debug("Killed pid '%s' after %s attempts.", pid, attempts)
|
||||
for leftover_fn in fork_fns.as_list():
|
||||
if sh.exists(leftover_fn):
|
||||
LOG.debug("Removing forking related file %r", (leftover_fn))
|
||||
sh.unlink(leftover_fn)
|
||||
else:
|
||||
msg = "Could not stop %r after %s attempts" % (app_name, attempts)
|
||||
raise excp.StopException(msg)
|
||||
|
||||
def status(self, app_name):
|
||||
# Attempt to find the status of a given app by finding where that apps
|
||||
# pid file is and loading said pids details (from stderr/stdout) files
|
||||
# that should exist as well as by using shell utilities to determine
|
||||
# if said pid is still running...
|
||||
trace_dir = self.runtime.get_option('trace_dir')
|
||||
if not sh.isdir(trace_dir):
|
||||
return (STATUS_UNKNOWN, '')
|
||||
(pid_file, stderr_fn, stdout_fn) = self._form_file_names(FORK_TEMPL % (app_name))
|
||||
pid = self._extract_pid(pid_file)
|
||||
fork_fns = self._form_file_names(app_name)
|
||||
pid = fork_fns.extract_pid()
|
||||
stderr = ''
|
||||
try:
|
||||
stderr = sh.load_file(stderr_fn)
|
||||
except IOError:
|
||||
stderr = sh.load_file(fork_fns.stderr)
|
||||
except (IOError, ValueError, TypeError):
|
||||
pass
|
||||
stdout = ''
|
||||
try:
|
||||
stdout = sh.load_file(stdout_fn)
|
||||
except IOError:
|
||||
stdout = sh.load_file(fork_fns.stdout)
|
||||
except (IOError, ValueError, TypeError):
|
||||
pass
|
||||
if pid and sh.is_running(pid):
|
||||
if pid is not None and sh.is_running(pid):
|
||||
return (STATUS_STARTED, (stdout + stderr).strip())
|
||||
else:
|
||||
return (STATUS_UNKNOWN, (stdout + stderr).strip())
|
||||
|
||||
def _form_file_names(self, file_name):
|
||||
def _form_file_names(self, app_name):
|
||||
# Form all files names which should be connected to the given forked application name
|
||||
fork_fn = FORK_TEMPL % (app_name)
|
||||
trace_dir = self.runtime.get_option('trace_dir')
|
||||
return (sh.joinpths(trace_dir, file_name + ".pid"),
|
||||
sh.joinpths(trace_dir, file_name + ".stderr"),
|
||||
sh.joinpths(trace_dir, file_name + ".stdout"))
|
||||
|
||||
def _do_trace(self, fn, kvs):
|
||||
trace_dir = self.runtime.get_option('trace_dir')
|
||||
run_trace = tr.TraceWriter(tr.trace_filename(trace_dir, fn))
|
||||
for (k, v) in kvs.items():
|
||||
run_trace.trace(k, v)
|
||||
return run_trace.filename()
|
||||
trace_fn = None
|
||||
if trace_dir:
|
||||
trace_fn = tr.trace_filename(trace_dir, fork_fn)
|
||||
base_fork_fn = sh.joinpths(trace_dir, fork_fn)
|
||||
return ForkFiles(pid=base_fork_fn + ".pid",
|
||||
stdout=base_fork_fn + ".stdout",
|
||||
stderr=base_fork_fn + ".stderr",
|
||||
trace=trace_fn)
|
||||
|
||||
def _begin_start(self, app_name, app_pth, app_wkdir, args):
|
||||
fn_name = FORK_TEMPL % (app_name)
|
||||
(pid_fn, stderr_fn, stdout_fn) = self._form_file_names(fn_name)
|
||||
trace_info = dict()
|
||||
trace_info[PID_FN] = pid_fn
|
||||
trace_info[STDERR_FN] = stderr_fn
|
||||
trace_info[STDOUT_FN] = stdout_fn
|
||||
trace_info[ARGS] = json.dumps(args)
|
||||
trace_fn = self._do_trace(fn_name, trace_info)
|
||||
fork_fns = self._form_file_names(app_name)
|
||||
trace_fn = fork_fns.trace
|
||||
# Ensure all arguments for this app in string format
|
||||
args = [str(i) for i in args if i is not None]
|
||||
if trace_fn:
|
||||
# Not needed, but useful to know where the files are located at
|
||||
#
|
||||
# TODO(harlowja): use this info instead of forming the filenames
|
||||
# repeatly
|
||||
trace_info = {}
|
||||
trace_info.update(fork_fns.as_dict())
|
||||
# Useful to know what args were sent along
|
||||
trace_info[ARGS] = json.dumps(args)
|
||||
run_trace = tr.TraceWriter(trace_fn)
|
||||
for (k, v) in trace_info.items():
|
||||
if v is not None:
|
||||
run_trace.trace(k, v)
|
||||
LOG.debug("Forking %r by running command %r with args (%s)" % (app_name, app_pth, " ".join(args)))
|
||||
with sh.Rooted(True):
|
||||
sh.fork(app_pth, app_wkdir, pid_fn, stdout_fn, stderr_fn, *args)
|
||||
sh.fork(app_pth, app_wkdir, fork_fns.pid, fork_fns.stdout, fork_fns.stderr, *args)
|
||||
return trace_fn
|
||||
|
||||
def _post_start(self, app_name):
|
||||
fork_fns = self._form_file_names(app_name)
|
||||
utils.log_iterable(fork_fns.as_list(),
|
||||
header="Forked %s with details in the following files" % (app_name),
|
||||
logger=LOG)
|
||||
|
||||
def start(self, app_name, app_pth, app_dir, opts):
|
||||
return self._begin_start(app_name, app_pth, app_dir, opts)
|
||||
trace_fn = self._begin_start(app_name, app_pth, app_dir, opts)
|
||||
self._post_start(app_name)
|
||||
return trace_fn
|
||||
|
||||
Reference in New Issue
Block a user