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
# Copyright (c) 2014, Facebook, Inc. All rights reserved. # # This source code is licensed under the BSD-style license found in the # LICENSE file in the root directory of this source tree. An additional grant # of patent rights can be found in the PATENTS file in the same directory. #
VService can be used directly, for example with `VService.initFromCLI()`, or it can be subclassed and used similarly. """
"""Core class for implementing services.""" metavar='TASK', nargs='*', help='Tasks to run. Pass without args to see the ' 'list. If not passed, all tasks will be ' 'started')
action='store_true', help='Start as a daemon using PIDFILE', ) default=DEFAULT_PID, help='Daemon pid file path [%(default)s]', metavar='PIDFILE', ) action='store_true', help='Kill the currently running daemon for PIDFILE', ) action='store_true', help='Output whether the daemon for PIDFILE is running', )
runit_install = option(action='store_true', help='Install this service under runit.')
# Option initialization
# Control variables
# Initialize Tasks # Register tasks from global instance self.tasks.register(t)
# Register additional tasks from service declaration
# Register warnings API
# Register exported values API
# Set start_time for aliveSince() calls
"""Override this to do any service-specific initialization"""
def _loptName(cls, name):
"""Processes "action" oriented options.""" if self.getOption('runit_install'): self._install()
# Act on the --status if present. We use getOption, since it # may return null if HAS_DAEMONIZE is False if self.getOption('status'): if daemon.status(pidfile=self.pidfile, logger=self.logger): sys.exit(0) else: sys.exit(1)
# Act on the --kill flag if present. We use getOption, since it # may return null if HAS_DAEMONIZE is False if self.getOption('kill'): if daemon.kill(pidfile=self.pidfile, logger=self.logger): sys.exit(0) else: sys.exit(1)
if self.options.tasks == []: print("Available Tasks:") for t in self.tasks: print(" - %s" % t.__name__) sys.exit(1)
# Determine which tasks need to be unregistered based on the # selected tasks. if t.__name__ not in selected_tasks]
# Unregister non-selected tasks self.tasks.unregister(t)
# Actually create the tasks
# Call service initialization hook after tasks have been instantiated, # but before they've been initialized.
# Initialize the tasks
assert signum in (signal.SIGINT, signal.SIGTERM) self.logger.info('signal -%d received', signum) self.shutdown()
# TODO: Should this be somewhere else? # Things seem to fail more gracefully if we trigger the stop # out of band (with a signal handler) instead of catching the # KeyboardInterrupt...
"""Returns a task for the given class `name` or type, or None.""" return self.tasks.get(name)
"""Returns a task for the given class `name` or type, or throws."""
"""Request a graceful shutdown. Does not block."""
"""Request a graceful restart. Does not block.""" self.logger.info("Received graceful restart request") self._restart = True self.stop()
# If there are no remaining tasks (or this service has no tasks) # just sleep until ^C is pressed except KeyboardInterrupt: self.logger.info('KeyboardInterrupt Received! Stopping Tasks...')
except KeyboardInterrupt: self.logger.warning('Abandon all hope ye who enter here')
"""Blocks until a stop is requested, waits for all tasks to shutdown""" while not self._stop: time.sleep(0.1) for t in reversed(self.tasks): t.join()
"""Starts this service, processing command line arguments.""" ap = cls._buildArgumentParser() ns = ap.parse_args() instance = cls.initFromOptions(ns, name=name) return instance
"""Starts this service, arguments from `ns`""" instance = cls(ns) if name is not None: instance.name = name instance.preprocessOptions()
if ns.daemon: daemon.daemonize( command=functools.partial(cls._runloop, instance), name=instance.name, pidfile=ns.pidfile, logger=instance.logger, )
else: return cls._runloop(instance)
def _runloop(cls, instance): while not instance._stop: try: instance._createTasks() instance._startTasks() except Exception: instance.logger.exception("Unexpected Exception during init") instance.shutdown()
instance._wait()
if instance._restart: instance = cls(instance.options)
instance.logger.info("Instance shut down gracefully")
"""Starts this service in the background
Returns a thread that will join() on graceful shutdown."""
def name(self):
def name(self, value):
"""Basic stderr logging. Override this to do something else."""
def _makeArgumentParser(cls): """Create an argparse.ArgumentParser instance.
Override this method if you already have an ArgumentParser instance to use or you simply want to specify some of the optional arguments to argparse.ArgumentParser.__init__ (e.g. "fromfile_prefix_chars" or "conflict_handler"...) """
def _buildArgumentParser(cls):
# TODO: Add each tasks' arguments to an argument group
def loglevel(self): # TODO: Deprecate this after proting args to proper option()s
return getattr(self.options, name, default)
setattr(self.options, name, value)
return self.options.__dict__
if not HAS_PSUTIL: raise NotImplementedError("You need psutil installed to install " "under runit") import sparts.runit sparts.runit.install(self.name) sys.exit(0)
else:
if matcher.match(key) is not None]
for key in keys]) |