Viewing file: service.py (11.67 KB) -rw-r--r-- Select action/file-type: (+) | (+) | (+) | Code (+) | Session (+) | (+) | SDB (+) | (+) | (+) | (+) | (+) | (+) |
# -*- test-case-name: twisted.application.test.test_service -*- # Copyright (c) Twisted Matrix Laboratories. # See LICENSE for details.
""" Service architecture for Twisted.
Services are arranged in a hierarchy. At the leafs of the hierarchy, the services which actually interact with the outside world are started. Services can be named or anonymous -- usually, they will be named if there is need to access them through the hierarchy (from a parent or a sibling).
Maintainer: Moshe Zadka """
from __future__ import absolute_import, division
from zope.interface import implementer, Interface, Attribute
from twisted.persisted import sob from twisted.python.reflect import namedAny from twisted.python import components from twisted.internet import defer from twisted.plugin import IPlugin
class IServiceMaker(Interface): """ An object which can be used to construct services in a flexible way.
This interface should most often be implemented along with L{twisted.plugin.IPlugin}, and will most often be used by the 'twistd' command. """ tapname = Attribute( "A short string naming this Twisted plugin, for example 'web' or " "'pencil'. This name will be used as the subcommand of 'twistd'.")
description = Attribute( "A brief summary of the features provided by this " "Twisted application plugin.")
options = Attribute( "A C{twisted.python.usage.Options} subclass defining the " "configuration options for this application.")
def makeService(options): """ Create and return an object providing L{twisted.application.service.IService}.
@param options: A mapping (typically a C{dict} or L{twisted.python.usage.Options} instance) of configuration options to desired configuration values. """
@implementer(IPlugin, IServiceMaker) class ServiceMaker(object): """ Utility class to simplify the definition of L{IServiceMaker} plugins. """ def __init__(self, name, module, description, tapname): self.name = name self.module = module self.description = description self.tapname = tapname
def options(): def get(self): return namedAny(self.module).Options return get, options = property(*options())
def makeService(): def get(self): return namedAny(self.module).makeService return get, makeService = property(*makeService())
class IService(Interface): """ A service.
Run start-up and shut-down code at the appropriate times.
@type name: C{string} @ivar name: The name of the service (or None) @type running: C{boolean} @ivar running: Whether the service is running. """
def setName(name): """ Set the name of the service.
@type name: C{str} @raise RuntimeError: Raised if the service already has a parent. """
def setServiceParent(parent): """ Set the parent of the service. This method is responsible for setting the C{parent} attribute on this service (the child service).
@type parent: L{IServiceCollection} @raise RuntimeError: Raised if the service already has a parent or if the service has a name and the parent already has a child by that name. """
def disownServiceParent(): """ Use this API to remove an L{IService} from an L{IServiceCollection}.
This method is used symmetrically with L{setServiceParent} in that it sets the C{parent} attribute on the child.
@rtype: L{Deferred<defer.Deferred>} @return: a L{Deferred<defer.Deferred>} which is triggered when the service has finished shutting down. If shutting down is immediate, a value can be returned (usually, C{None}). """
def startService(): """ Start the service. """
def stopService(): """ Stop the service.
@rtype: L{Deferred<defer.Deferred>} @return: a L{Deferred<defer.Deferred>} which is triggered when the service has finished shutting down. If shutting down is immediate, a value can be returned (usually, C{None}). """
def privilegedStartService(): """ Do preparation work for starting the service.
Here things which should be done before changing directory, root or shedding privileges are done. """
@implementer(IService) class Service(object): """ Base class for services.
Most services should inherit from this class. It handles the book-keeping responsibilities of starting and stopping, as well as not serializing this book-keeping information. """
running = 0 name = None parent = None
def __getstate__(self): dict = self.__dict__.copy() if "running" in dict: del dict['running'] return dict
def setName(self, name): if self.parent is not None: raise RuntimeError("cannot change name when parent exists") self.name = name
def setServiceParent(self, parent): if self.parent is not None: self.disownServiceParent() parent = IServiceCollection(parent, parent) self.parent = parent self.parent.addService(self)
def disownServiceParent(self): d = self.parent.removeService(self) self.parent = None return d
def privilegedStartService(self): pass
def startService(self): self.running = 1
def stopService(self): self.running = 0
class IServiceCollection(Interface): """ Collection of services.
Contain several services, and manage their start-up/shut-down. Services can be accessed by name if they have a name, and it is always possible to iterate over them. """
def getServiceNamed(name): """ Get the child service with a given name.
@type name: C{str} @rtype: L{IService} @raise KeyError: Raised if the service has no child with the given name. """
def __iter__(): """ Get an iterator over all child services. """
def addService(service): """ Add a child service.
Only implementations of L{IService.setServiceParent} should use this method.
@type service: L{IService} @raise RuntimeError: Raised if the service has a child with the given name. """
def removeService(service): """ Remove a child service.
Only implementations of L{IService.disownServiceParent} should use this method.
@type service: L{IService} @raise ValueError: Raised if the given service is not a child. @rtype: L{Deferred<defer.Deferred>} @return: a L{Deferred<defer.Deferred>} which is triggered when the service has finished shutting down. If shutting down is immediate, a value can be returned (usually, C{None}). """
@implementer(IServiceCollection) class MultiService(Service): """ Straightforward Service Container.
Hold a collection of services, and manage them in a simplistic way. No service will wait for another, but this object itself will not finish shutting down until all of its child services will finish. """
def __init__(self): self.services = [] self.namedServices = {} self.parent = None
def privilegedStartService(self): Service.privilegedStartService(self) for service in self: service.privilegedStartService()
def startService(self): Service.startService(self) for service in self: service.startService()
def stopService(self): Service.stopService(self) l = [] services = list(self) services.reverse() for service in services: l.append(defer.maybeDeferred(service.stopService)) return defer.DeferredList(l)
def getServiceNamed(self, name): return self.namedServices[name]
def __iter__(self): return iter(self.services)
def addService(self, service): if service.name is not None: if service.name in self.namedServices: raise RuntimeError("cannot have two services with same name" " '%s'" % service.name) self.namedServices[service.name] = service self.services.append(service) if self.running: # It may be too late for that, but we will do our best service.privilegedStartService() service.startService()
def removeService(self, service): if service.name: del self.namedServices[service.name] self.services.remove(service) if self.running: # Returning this so as not to lose information from the # MultiService.stopService deferred. return service.stopService() else: return None
class IProcess(Interface): """ Process running parameters.
Represents parameters for how processes should be run. """ processName = Attribute( """ A C{str} giving the name the process should have in ps (or C{None} to leave the name alone). """)
uid = Attribute( """ An C{int} giving the user id as which the process should run (or C{None} to leave the UID alone). """)
gid = Attribute( """ An C{int} giving the group id as which the process should run (or C{None} to leave the GID alone). """)
@implementer(IProcess) class Process: """ Process running parameters.
Sets up uid/gid in the constructor, and has a default of C{None} as C{processName}. """ processName = None
def __init__(self, uid=None, gid=None): """ Set uid and gid.
@param uid: The user ID as whom to execute the process. If this is C{None}, no attempt will be made to change the UID.
@param gid: The group ID as whom to execute the process. If this is C{None}, no attempt will be made to change the GID. """ self.uid = uid self.gid = gid
def Application(name, uid=None, gid=None): """ Return a compound class.
Return an object supporting the L{IService}, L{IServiceCollection}, L{IProcess} and L{sob.IPersistable} interfaces, with the given parameters. Always access the return value by explicit casting to one of the interfaces. """ ret = components.Componentized() availableComponents = [MultiService(), Process(uid, gid), sob.Persistent(ret, name)]
for comp in availableComponents: ret.addComponent(comp, ignoreClass=1) IService(ret).setName(name) return ret
def loadApplication(filename, kind, passphrase=None): """ Load Application from a given file.
The serialization format it was saved in should be given as C{kind}, and is one of C{pickle}, C{source}, C{xml} or C{python}. If C{passphrase} is given, the application was encrypted with the given passphrase.
@type filename: C{str} @type kind: C{str} @type passphrase: C{str} """ if kind == 'python': application = sob.loadValueFromFile(filename, 'application', passphrase) else: application = sob.load(filename, kind, passphrase) return application
__all__ = ['IServiceMaker', 'IService', 'Service', 'IServiceCollection', 'MultiService', 'IProcess', 'Process', 'Application', 'loadApplication']
|