Coverage for janitoo.runner : 37%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: utf-8 -*-
Start a worker or a broker as a daemon.
Must be updated to work with multiple workers.
What do we need :
* the user ou userid * the log file * the error output * the standard output * the pid file * the working directory
Based on the runner of python-daemon :
* Copyright © 2009–2010 Ben Finney <ben+python@benfinney.id.au> * Copyright © 2007–2008 Robert Niederreiter, Jens Klein * Copyright © 2003 Clark Evans * Copyright © 2002 Noah Spurrier * Copyright © 2001 Jürgen Hermann
"""
This file is part of Janitoo.
Janitoo is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
Janitoo is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with Janitoo. If not, see <http://www.gnu.org/licenses/>. """
# Set default logging handler to avoid "No handler found" warnings. #We must NOT subsitute % in value for alembic (database section)
""" Abstract base class for errors. """
""" Raised when specified action is invalid. """
""" Raised when failure starting. """
""" Raised when failure stopping. """
""" Controller for a callable running in a separate background process.
The first command-line argument is the action to take:
* 'start': Become a daemon and call `app.run()`. * 'stop': Exit the daemon process specified in the PID file. * 'restart': Stop, then start. * 'status': Show the status of the process.
"""
""" Set up the parameters of a new runner.
The `app` argument must have the following attributes:
* `stdin_path`, `stdout_path`, `stderr_path`: Filesystem paths to open and replace the existing `sys.stdin`, `sys.stdout`, `sys.stderr`.
* `pidfile_path`: Absolute filesystem path to a file that will be used as the PID file for the daemon. If ``None``, no PID file will be used.
* `pidfile_timeout`: Used as the default acquisition timeout value supplied to the runner's PID lock file.
""" self.options = {} self.pidfile_timeout = 6.0 self.stdout_path = None self.stderr_path = None self.pidfile_path = None self.userid = None self.args = None self.parse_args() self.pidfile = None self.pidfile_path = os.path.join(self.options['pid_dir'], self.options['service'] + ".pid") self.pidfile = make_pidlockfile( self.pidfile_path, self.pidfile_timeout) if self.pidfile.is_locked() and not is_pidfile_stale(self.pidfile) \ and self.action == 'start': print("Process already running. Exiting.") sys.exit(1) if (not self.pidfile.is_locked() or is_pidfile_stale(self.pidfile)) \ and (self.action == 'stop' or self.action == 'kill'): print("Process not running. Exiting.") sys.exit(1)
""" The running process of the application """ raise RunnerInvalidActionError("Action: %(action)r is not implemented" % vars(self))
""" The shutdown process of the application """ raise RunnerInvalidActionError("Action: %(action)r is not implemented" % vars(self))
""" Emit a usage message, then exit. """ usage_exit_code = 2 message = "usage: use --help to get help" % vars() emit_message(message) sys.exit(usage_exit_code)
""" Parse command-line arguments. """ args = jnt_parse_args() self.options = vars(args) self.action = args.command self.args = args self.stdout_path = os.path.join(self.options['log_dir'], self.options['service'] + "_out.log") self.stderr_path = os.path.join(self.options['log_dir'], self.options['service'] + "_err.log") self.pidfile_path = os.path.join(self.options['pid_dir'], self.options['service'] + ".pid") if self.options['user'] and self.options['user'] != "": self.userid = pwd.getpwnam(self.options['user']).pw_uid if self.userid != os.getuid(): #print self.userid os.setuid(self.userid) try: os.makedirs(self.options['pid_dir']) os.makedirs(self.options['home_dir']) os.makedirs(self.options['conf_dir']) os.makedirs(self.options['log_dir']) except OSError as exc: # Python >2.5 if exc.errno == errno.EEXIST: pass else: raise if self.action not in self.action_funcs: self._usage_exit(args)
""" Open the daemon context and run the application. """ self.daemon_context = DaemonContext() self.daemon_context.pidfile = self.pidfile #self.daemon_context.stdin = open(stdin_path, 'r') self.daemon_context.stdout = open(self.stdout_path, 'w+') self.daemon_context.stderr = open(self.stderr_path, 'w+', buffering=0) if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() #print "Here" try: self.daemon_context.open() except TimeoutPIDLockFile.AlreadyLocked: pidfile_path = self.pidfile.path raise RunnerStartFailureError( "PID file %(pidfile_path)r already locked" % vars()) #print "Here 2" message = self.start_message % os.getpid() logger.info(message) emit_message(message, self.daemon_context.stdout) self.app_run()
""" Open the daemon context and run the application. """ if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() self.pidfile.__enter__() message = self.start_message % os.getpid() emit_message(message, sys.stdout) try: self.app_run() except KeyboardInterrupt: pass finally: if self.pidfile: try: self.pidfile.__exit__(None, None, None) except: pass self.app_shutdown()
""" Terminate the daemon process specified in the current PID file. """ pid = self.pidfile.read_pid() try: os.kill(pid, signal.SIGTERM) except OSError as exc: raise RunnerStopFailureError( "Failed to terminate %(pid)d: %(exc)s" % vars())
""" Terminate the daemon process specified in the current PID file. """ pid = self.pidfile.read_pid() try: os.kill(pid, signal.SIGKILL) except OSError, exc: raise RunnerStopFailureError( "Failed to kill %(pid)d: %(exc)s" % vars())
""" Exit the daemon process specified in the current PID file. """ if self.pidfile.is_locked(): if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() else: self._terminate_daemon_process()
"""Kill the daemon process specified in the current PID file. """ if not self.pidfile.is_locked(): pidfile_path = self.pidfile.path raise RunnerStopFailureError( "PID file %(pidfile_path)r not locked" % vars())
if is_pidfile_stale(self.pidfile): self.pidfile.break_lock() else: self._kill_daemon_process()
"""Show the status of the process. """ if not self.pidfile.is_locked() or is_pidfile_stale(self.pidfile): emit_message(self.status_message_not_running) else: emit_message(self.status_message_running % self.pidfile.read_pid())
"""Stop, then start. """ self._stop() time.sleep(self.pidfile_timeout) self._start()
"""Reload application configuration. """ pid = self.pidfile.read_pid() try: os.kill(pid, signal.SIGHUP) except OSError as exc: raise RunnerStopFailureError( "Failed to reload %(pid)d: %(exc)s" % vars())
"""Flush data to disk via SIGUSR1 signal """ pid = self.pidfile.read_pid() try: os.kill(pid, signal.SIGUSR1) except OSError as exc: raise RunnerStopFailureError( "Failed to flush %(pid)d: %(exc)s" % vars())
'start': _start, 'stop': _stop, 'restart': _restart, 'reload': _reload, 'kill': _kill, 'status': _status, 'flush': _flush, 'front': _front, }
"""Return the function for the specified action.
Raises ``RunnerInvalidActionError`` if the action is unknown.
""" try: func = self.action_funcs[self.action] except KeyError: raise RunnerInvalidActionError( "Unknown action: %(action)r" % vars(self)) return func
"""Perform the requested action. """ func = self._get_action_func() func(self)
"""Emit a message to the specified stream (default `sys.stderr`). """ if stream is None: stream = sys.stderr stream.write("%(message)s\n" % vars()) stream.flush()
"""Make a PIDLockFile instance with the given filesystem path. """ if not isinstance(path, basestring): error = ValueError("Not a filesystem path: %(path)r" % vars()) raise error if not os.path.isabs(path): error = ValueError("Not an absolute path: %(path)r" % vars()) raise error lockfile = TimeoutPIDLockFile(path, acquire_timeout) return lockfile
"""Determine whether a PID file is stale.
Return ``True`` (“stale”) if the contents of the PID file are valid but do not match the PID of a currently-running process; otherwise return ``False``. """ result = False pidfile_pid = pidfile.read_pid() if pidfile_pid is not None: try: os.kill(pidfile_pid, signal.SIG_DFL) except OSError, exc: if exc.errno == errno.ESRCH: # The specified PID does not exist result = True return result
"""Default argument parser """ # Turn off help, so we print all options in response to -h add_help=False ) help="The configuration file", metavar="FILE") "hostname" : socket.gethostname(), "service" : "jnt_generic", "user" : "janitoo", "log_dir" : "/tmp/jnt_logs", "home_dir" : "/tmp/jnt_home", "cache_dir" : "/tmp/jnt_cache", "pid_dir" : "/tmp/jnt_run", "conf_dir" : "/tmp/jnt_conf", "broker_ip" : "127.0.0.1", "broker_port" : "1883", "broker_user" : "", "broker_password" : "", } raise IOError("Can't find configuration file %s"%(args.conf_file)) # Don't surpress add_help here so it will handle -h # Inherit options from config_parser parents=[conf_parser], # print script description with -h/--help description=__doc__, # Don't mess with format of description formatter_class=argparse.RawDescriptionHelpFormatter, ) start start the service stop stop the service restart restart the service reload reload the service kill kill the service status status of the service flush flush data to disk front don't run as daemon """ choices=('start', 'stop', 'restart', 'reload', 'flush', 'kill', 'status', 'front'), help=helpCommand) #parser.add_argument("--conf_file", help="the hostname used by the service. Leave blank to auto-configure.")
|