Source code for ncolony.service

# Copyright (c) Moshe Zadka
# See LICENSE for details.
"""ncolony.service
==========================================================================

Implement the 'ncolony' twistd plugin.

.. code-block:: bash

  $ twistd ncolony --config <dir> --messages <dir>

Will run a service that brings up all processes described in files
in the configuration directory (and shuts them down if the files
ago away), and listens for restart messages on the messages directory.
"""

from twisted.python import usage
from twisted.application import service as taservice, internet
from twisted.runner import procmon as procmonlib, procmontap

from ncolony import directory_monitor, process_events

## pylint: disable=too-few-public-methods

[docs]class TransportDirectoryDict(dict): """Dict-like object that writes the 'pid' value to a directory This dict-like object assumes all the values have a 'pid' attribute, and writes that attribute into a file named the same as the key in the given directory. """ def __init__(self, output): """Initialize :param output: a {twisted.python.filepath.FilePath} object """ super(TransportDirectoryDict, self).__init__() self.output = output def __setitem__(self, name, value): super(TransportDirectoryDict, self).__setitem__(name, value) self.output.child(name).setContent(str(value.pid)) def __delitem__(self, name): super(TransportDirectoryDict, self).__delitem__(name) self.output.child(name).remove()
## pylint: enable=too-few-public-methods
[docs]def get(config, messages, freq, pidDir=None, reactor=None): """Return a service which monitors processes based on directory contents Construct and return a service that, when started, will run processes based on the contents of the 'config' directory, restarting them if file contents change and stopping them if the file is removed. It also listens for restart and restart-all messages on the 'messages' directory. :param config: string, location of configuration directory :param messages: string, location of messages directory :param freq: number, frequency to check for new messages and configuration updates :param pidDir: {twisted.python.filepath.FilePath} or None, location to keep pid files :param reactor: something implementing the interfaces {twisted.internet.interfaces.IReactorTime} and {twisted.internet.interfaces.IReactorProcess} and :returns: service, {twisted.application.interfaces.IService} """ ret = taservice.MultiService() args = () if reactor is not None: args = reactor, procmon = procmonlib.ProcessMonitor(*args) if pidDir is not None: protocols = TransportDirectoryDict(pidDir) procmon.protocols = protocols procmon.setName('procmon') receiver = process_events.Receiver(procmon) confcheck = directory_monitor.checker(config, receiver) confserv = internet.TimerService(freq, confcheck) confserv.setServiceParent(ret) messagecheck = directory_monitor.messages(messages, receiver) messageserv = internet.TimerService(freq, messagecheck) messageserv.setServiceParent(ret) procmon.setServiceParent(ret) return ret
## pylint: disable=too-few-public-methods
[docs]class Options(usage.Options): """Options for ncolony service""" optParameters = [ ["config", None, None, "Directory for configuration"], ["messages", None, None, "Directory for messages"], ["frequency", None, 10, "Frequency of checking for updates", float], ["pid", None, None, "Directory of PID files"], ] + procmontap.Options.optParameters
[docs] def postOptions(self): """Checks that required messages/config directories are present""" for param in ('messages', 'config'): if self[param] is None: raise usage.UsageError("Missing required", param)
## pylint: enable=too-few-public-methods
[docs]def makeService(opt): """Return a service based on parsed command-line options :param opt: dict-like object. Relevant keys are config, messages, pid, frequency, threshold, killtime, minrestartdelay and maxrestartdelay :returns: service, {twisted.application.interfaces.IService} """ ret = get(config=opt['config'], messages=opt['messages'], pidDir=opt['pid'], freq=opt['frequency']) pm = ret.getServiceNamed("procmon") pm.threshold = opt["threshold"] pm.killTime = opt["killtime"] pm.minRestartDelay = opt["minrestartdelay"] pm.maxRestartDelay = opt["maxrestartdelay"] return ret