Beispiel #1
0
def main():
    # Let's read the command line
    usage = "usage: %prog [options]" # add 'arg1 arg2' etc as required
    parser = FilteredOptionParser(usage=usage, version="Peloton version %s" % peloton.RELEASE_VERSION)
#    parser.set_defaults(nodetach=False)
    parser.add_option("--prefix",
                     help="Prefix directory to help with setting paths")
    
    parser.add_option("--nodetach",
                      action="store_true",
                      default=False,
                      help="Prevent PSC detaching from terminal [default: %default]")
    
    parser.add_option("-c", "--configfile",
                      help="Path to configuration file for the PSC [default: %default].",
                      dest="configfile",
                      default='psc.pcfg')
    
    parser.add_option("-b", "--bind", 
                      help="""specify the host:port to which this instance should bind. Overides
values set in configuration.""",
                      dest='bindhost')
    
    parser.add_option("--anyport",
                      action="store_true",
                      default=False,
                      help="""When set, this permits the PSC to seek a free port if the 
configured port is not available.""")
    
    parser.add_option("-s", "--servicepath",
                      help="""Directory containing peloton services. You may specify several
such directories with multiple instances of this option [default: %default]""",
                      action="append",
                      default=["$PREFIX/service"])
        
    parser.add_option("--loglevel",
                      help="""Set the logging level to one of critical, fatal, error(uat, prod), warning, info(test), debug(dev).
(defaults for each run mode indicated in brackets).""",
                      choices=['critical', 'fatal', 'error', 'warning', 'info', 'debug'])
    
    parser.add_option("--logdir", 
                      help="""Directory to which log files should be written. By setting this argument
the logging-to-file system will be enabled.""")
    parser.add_option("--disable",
                      help="""Comma delimited list of plugins to prevent starting even if configuration has them enabled""",
                      action="append")
    parser.add_option("--enable",
                      help="""Comma delimited list of plugins to start even if configuration has them disabled""",
                      action="append")
    
    parser.add_option("--flags",
                      help="""Comma delimited list of flags to add to this PSC.""",
                      action="append")
    
    
    options, args = parser.parse_args()
    # Handling errors and pumping back through the system
    #if len(args) != 1:
    #    parser.error("incorrect number of arguments")

    # add any sevice directories to sys.path if not already there
    for sd in options.servicepath:
        if sd not in sys.path:
            sys.path.append(sd)


    # enable, disable and flags are all 'append' types, but allow
    # comma delimited entries as well so we need to turn the list of
    # n potentially compound entries into a single list
    
    if options.enable:
        options.enable = deCompound(options.enable)
    else:
        options.enable=[]
        
    if options.disable:
        options.disable = deCompound(options.disable)
    else:
        options.disable=[]

    if options.flags:
        options.flags = deCompound(options.flags)
    else:
        options.flags=[]

    # determine the appropriate log-level for the root logger based
    # on supplied arguments.
    if options.loglevel:
        options.loglevel = options.loglevel.upper()
    else:
        options.loglevel = "ERROR"

    # Load configuration from file
    pc = PelotonSettings()
    try:
        pc.load(options.configfile)
    except IOError:
        sys.stderr.write("There is no profile for the PSC!\n")
        return 1
    
    if pc.profile.has_key('flags'):
        pc.profile.flags.extend(options.flags)
    else:
        pc.profile.flags = options.flags
        
    # copy in the necessary from options to config
    keys =['configfile', 'nodetach', 'bindhost', 
           'anyport', 'servicepath', 
           'loglevel', 'logdir', 
           'enable', 'disable']
    for k in keys:
        pc[k] = getattr(options, k)
        
    try:
        exitCode = start(pc)
    except:
        logging.getLogger().exception('Untrapped error in PSC: ')
        exitCode = 99
        
    return exitCode
Beispiel #2
0
class PelotonService(object):
    """ Base class for all services. Public methods all have names prefixed
'public_', much as twisted spread remote callable methods are prefixed 'remote_'.

Configuration
=============

Services live in a strictly regimented structure on the file system. This simplifies
auto-generation of code and automated loading with minimal magic.

The root path points to a directory which contains the service directory. 
The service directory is laid out as follows, where the service is 
called FooBar::

    service_root/foobar/config/service.pcfg
                       /foobar.py
                       /<supporting code>
                       /resource/...

Note that nomenclature is relatively simple; the service directory must be 
named the same as the service shortname (when lowercased). The configuration
files are named '*.pcfg'.

The service directory must contain at the very least a file called foobar.py (note
lower case) containing the class FooBar(PelotonService,...). Here FooBar retains
it's original capitalisation and, indeed, it is a matter of convention
that the service name should be camel case.            
"""
    def __init__(self, name, dispatcher, logger):
        """ homePath passed in on construction because from this module
cannot find where the concrete sub-class lives. Configurations are found relative to this
homePath in homePath/config. 
"""
        self.name = name
        self.dispatcher = dispatcher
        self.logger = logger

    def initSupportServices(self):
        """ Start supporting services, such as the logger. Kept out of __init__
so that we can instantiate very lightly (as required by the launch sequencer.)"""
        self.logger = logging.getLogger(self.name)
        
    def loadConfig(self, servicePath, runconfig=None):
        """ Load service configuration file and then augment with details
of all the methods in this service classed as 'public'. 

In doing this the attributes assigned by any decorators on the public methods
are checked out, especially the transform chain. Defaults are assigned
to, for example, HTML and XML output keys if none has been specified. Standard
templates are sought out on the filesystem and attached where found and
the @template keyword is substituted for in the transform chain.

runconfig is the name of a run-time configuration file specified at the time of launching
(either relative to the service config dir or an absolute path). This can specify
many things including, for example, the name under which to publish this service
and the location of the resource folder.

Developers should not use the logger here: loadConfig should be useable prior
to initSupportServices having been called."""
        servicePath, self.settings = locateService(self.name, servicePath, runconfig=runconfig)
        if self.settings.has_key('profile'):
            self.profile = self.settings.profile
        else:
            self.profile = PelotonSettings()
        self.profile['_sysRunConfig'] = runconfig
        if not self.profile.has_key('publishedName'):
            self.profile['publishedName'] = self.name

        publicMethods = [m for m in dir(self) if m.startswith('public_') and callable(getattr(self, m))]
        
        if not self.profile.has_key('methods'):
            self.profile['methods'] = PelotonSettings()

        methods = self.profile['methods']
        for nme in publicMethods:
            mthd = getattr(self, nme)
            shortname = nme[7:]

            templateTargets = findTemplateTargetsFor(self.profile['resourceRoot'], self.name, shortname)
            if hasattr(mthd, "_PELOTON_METHOD_PROPS"):
                properties = mthd._PELOTON_METHOD_PROPS
            else:
                properties = PelotonSettings()

            # step one, find all template files and insert
            # into transforms
            for target, templateFile in templateTargets:
                key = "transform.%s" % target
                if properties.has_key(key):
                    # look for @template and substitute
                    if "@template" in properties[key]:
                        properties[key][properties[key].index("@template")] \
                            = "template('%s')" % templateFile
                else:
                    # insert an entry
                    properties['transform.%s'%target] = \
                        ["template('%s')" % templateFile]
            
            # step two insert defaults for any empty transforms that
            # need a little something
            defaultTransforms = {'xml' : 'defaultXMLTransform',
                                 'html' : 'defaultHTMLTransform',
                                 'json' : 'jsonTransform'
                          }
            for target in ['xml', 'html', 'json']:
                key = "transform.%s" % target
                if not properties.has_key(key) \
                    or properties[key] == []:
                    properties[key]=[defaultTransforms[target]]
                    
            # step three, look for all transforms which still have
            # @transform in the chain and replace with defaults
            for k, v in properties.items():
                if not k.startswith('transform.'):
                    continue
                target = k[10:]
                if "@template" in v:
                    try:
                        v[v.index('@template')] = defaultTransforms[target]
                    except:
                        v[v.index('@template')] = ''

            if not methods.has_key(shortname):
                methods[shortname] = PelotonSettings()
            record = methods[shortname]
            record['doc'] = mthd.__doc__
            record['properties'] = str(properties)
            
        self.version = self.profile['version']
        
    def start(self):
        """ Executed after configuration is loaded, prior to starting work.
Can be used to setup database pools etc. Overide in actual services. """
        pass
    
    def stop(self):
        """ Executed prior to shuting down this service or the node.
Can be used to cleanup database pools etc. Overide in actual services. """
        pass
    
    def register(self, key, method, exchange='events', inThread=True):
        """ Registers method (which must have signature msg, exchange,
key, ctag) to be the target for events on exchange with key matching the 
specified pattern. By default inThread is True which means the event
handler will be called in a thread. If set False the event will be handled
in the main event loop so care must be taken not to perform long-running
operations in handlers that operate in this manner.

The handler class is returned by this method; keeping a reference to it
enables the service to de-register the handler subsequently."""
        class ServiceMethodHandler(pb.Referenceable):
            def __init__(self, handler, inThread=True):
                self.handler = handler
                self.inThread = inThread
                
            def remote_eventReceived(self, msg, exchange, key, ctag):
                if self.inThread:
                    reactor.callInThread(self.handler, msg, exchange, key, ctag)
                else:
                    self.handler(msg, exchange, key, ctag)

        handler = ServiceMethodHandler(method, inThread)
        reactor.callFromThread(self.dispatcher.register, key, handler, exchange)
        return handler
            
    def deregister(self, handler):
        """De-register this event handler. """
        reactor.callFromThread(self.dispatcher.deregister,handler)
            
    def fireEvent(self, key, exchange='events', **kwargs):
        """ Fire an event on the event bus. """
        reactor.callFromThread(
                self.dispatcher.fireEvent, key, exchange, **kwargs)
            
    def public_index(self):
        """ Default index page for HTTP connections. """
        return "There is no index for this service!"
Beispiel #3
0
    def loadConfig(self, servicePath, runconfig=None):
        """ Load service configuration file and then augment with details
of all the methods in this service classed as 'public'. 

In doing this the attributes assigned by any decorators on the public methods
are checked out, especially the transform chain. Defaults are assigned
to, for example, HTML and XML output keys if none has been specified. Standard
templates are sought out on the filesystem and attached where found and
the @template keyword is substituted for in the transform chain.

runconfig is the name of a run-time configuration file specified at the time of launching
(either relative to the service config dir or an absolute path). This can specify
many things including, for example, the name under which to publish this service
and the location of the resource folder.

Developers should not use the logger here: loadConfig should be useable prior
to initSupportServices having been called."""
        servicePath, self.settings = locateService(self.name, servicePath, runconfig=runconfig)
        if self.settings.has_key('profile'):
            self.profile = self.settings.profile
        else:
            self.profile = PelotonSettings()
        self.profile['_sysRunConfig'] = runconfig
        if not self.profile.has_key('publishedName'):
            self.profile['publishedName'] = self.name

        publicMethods = [m for m in dir(self) if m.startswith('public_') and callable(getattr(self, m))]
        
        if not self.profile.has_key('methods'):
            self.profile['methods'] = PelotonSettings()

        methods = self.profile['methods']
        for nme in publicMethods:
            mthd = getattr(self, nme)
            shortname = nme[7:]

            templateTargets = findTemplateTargetsFor(self.profile['resourceRoot'], self.name, shortname)
            if hasattr(mthd, "_PELOTON_METHOD_PROPS"):
                properties = mthd._PELOTON_METHOD_PROPS
            else:
                properties = PelotonSettings()

            # step one, find all template files and insert
            # into transforms
            for target, templateFile in templateTargets:
                key = "transform.%s" % target
                if properties.has_key(key):
                    # look for @template and substitute
                    if "@template" in properties[key]:
                        properties[key][properties[key].index("@template")] \
                            = "template('%s')" % templateFile
                else:
                    # insert an entry
                    properties['transform.%s'%target] = \
                        ["template('%s')" % templateFile]
            
            # step two insert defaults for any empty transforms that
            # need a little something
            defaultTransforms = {'xml' : 'defaultXMLTransform',
                                 'html' : 'defaultHTMLTransform',
                                 'json' : 'jsonTransform'
                          }
            for target in ['xml', 'html', 'json']:
                key = "transform.%s" % target
                if not properties.has_key(key) \
                    or properties[key] == []:
                    properties[key]=[defaultTransforms[target]]
                    
            # step three, look for all transforms which still have
            # @transform in the chain and replace with defaults
            for k, v in properties.items():
                if not k.startswith('transform.'):
                    continue
                target = k[10:]
                if "@template" in v:
                    try:
                        v[v.index('@template')] = defaultTransforms[target]
                    except:
                        v[v.index('@template')] = ''

            if not methods.has_key(shortname):
                methods[shortname] = PelotonSettings()
            record = methods[shortname]
            record['doc'] = mthd.__doc__
            record['properties'] = str(properties)
            
        self.version = self.profile['version']
Beispiel #4
0
 def __init__(self, *args, **kwargs):
     PelotonSettings.__init__(self, *args, **kwargs)
     self.__profiles = {}
     
     # holds transform chains as evaluated on the fly in coreio
     self.__outputTransforms = {}