added a queueable agent that will only execute one job at a time
git-svn-id: svn://svn.cy55.de/Zope3/src/cybertools/trunk@2483 fd906abe-77d9-0310-91a1-e0d9ade77398
This commit is contained in:
parent
998cbf59cd
commit
3f69f638d3
10 changed files with 181 additions and 36 deletions
|
@ -106,7 +106,7 @@ the path to the configuration file.
|
|||
>>> configFile.close()
|
||||
|
||||
>>> master.config
|
||||
controller.name = 'sample'
|
||||
controller.name = 'base.sample'
|
||||
logger.name = 'default'
|
||||
logger.standard = 30
|
||||
scheduler.name = 'sample'
|
||||
|
@ -155,12 +155,10 @@ is just one scheduler associated with the master agent.
|
|||
>>> master.scheduler
|
||||
<cybertools.agent.base.schedule.Scheduler object ...>
|
||||
|
||||
We schedule a sample job by taking the role of the controller and simply
|
||||
call the master agent's callback method for entering jobs.
|
||||
We schedule a sample job by calling an internal method of the agent's
|
||||
controller.
|
||||
|
||||
>>> from cybertools.agent.base.control import JobSpecification
|
||||
>>> jobSpec = JobSpecification('sample', '00001', agent='sample01')
|
||||
>>> master.setupJobs([jobSpec])
|
||||
>>> master.controllers[0].enterJob('sample', 'sample01')
|
||||
Job 00001 on agent sample01 has been executed.
|
||||
|
||||
Logging
|
||||
|
@ -173,15 +171,14 @@ Logging
|
|||
|
||||
>>> master.config.logger.standard = 20
|
||||
>>> master.logger.setup()
|
||||
>>> jobSpec = JobSpecification('sample', '00002', agent='sample01')
|
||||
>>> master.setupJobs([jobSpec])
|
||||
>>> master.controllers[0].enterJob('sample', 'sample01')
|
||||
Job 00002 on agent sample01 has been executed.
|
||||
2... agent:sample01 job:00002 message:job execution
|
||||
2... agent:sample01 job:00002 message:job execution result:OK
|
||||
|
||||
>>> for r in master.logger.records:
|
||||
... print r
|
||||
2... agent:sample01 job:00001 message:job execution
|
||||
2... agent:sample01 job:00002 message:job execution
|
||||
2... agent:sample01 job:00001 message:job execution result:OK
|
||||
2... agent:sample01 job:00002 message:job execution result:OK
|
||||
|
||||
|
||||
Using the Twisted-based Scheduler
|
||||
|
@ -190,27 +187,29 @@ Using the Twisted-based Scheduler
|
|||
By specifying the core scheduler in the agent's configuration this will be
|
||||
used automatically for scheduling.
|
||||
|
||||
In addition, we use another sample controller, now also the twisted-based
|
||||
from the core package. This will in turn set up a queueable agent from
|
||||
the core package so that now everything is running under the control of
|
||||
the twisted reactor.
|
||||
|
||||
>>> config = '''
|
||||
... controller(name='sample')
|
||||
... controller(name='core.sample')
|
||||
... scheduler(name='core')
|
||||
... logger(name='default', standard=30)
|
||||
... '''
|
||||
>>> master = Master(config)
|
||||
>>> master.setup()
|
||||
|
||||
>>> master.scheduler
|
||||
<cybertools.agent.core.schedule.Scheduler object ...>
|
||||
|
||||
We trigger the controller's setup as above and enter the same
|
||||
job specification.
|
||||
We enter the same job specification as above.
|
||||
|
||||
>>> master.setup()
|
||||
|
||||
>>> jobSpec = JobSpecification('sample', '00001', agent='sample01')
|
||||
>>> master.setupJobs([jobSpec])
|
||||
>>> master.controllers[0].enterJob('sample', 'sample01')
|
||||
|
||||
Now the job is not executed immediately - we have to hand over control to
|
||||
the twisted reactor first. The running of the reactor is simulated by
|
||||
a method provided for testing.
|
||||
the ``iterate()`` method provided for testing.
|
||||
|
||||
>>> from cybertools.agent.tests import tester
|
||||
>>> tester.iterate()
|
||||
|
|
|
@ -5,4 +5,4 @@ $Id$
|
|||
# register default adapters
|
||||
|
||||
from cybertools.agent.base import agent, control, job, log, schedule
|
||||
from cybertools.agent.core import schedule
|
||||
from cybertools.agent.core import agent, control, schedule
|
||||
|
|
|
@ -47,6 +47,10 @@ class Agent(object):
|
|||
def execute(self, job, params=None):
|
||||
pass
|
||||
|
||||
def log(self, job, result='OK'):
|
||||
self.logger.log(dict(message='job execution', job=job.identifier,
|
||||
agent=self.name, result=result))
|
||||
|
||||
|
||||
class Master(Agent):
|
||||
|
||||
|
@ -84,12 +88,11 @@ class Master(Agent):
|
|||
|
||||
class SampleAgent(Agent):
|
||||
|
||||
def execute(self, job, params=None):
|
||||
def send(self, job):
|
||||
self.execute(job)
|
||||
|
||||
def execute(self, job):
|
||||
print 'Job %s on agent %s has been executed.' % (job.identifier, self.name)
|
||||
self.log(job)
|
||||
|
||||
def log(self, job):
|
||||
self.logger.log(dict(message='job execution', job=job.identifier,
|
||||
agent=self.name))
|
||||
|
||||
agents.register(SampleAgent, Master, name='sample')
|
||||
agents.register(SampleAgent, Master, name='base.sample')
|
||||
|
|
|
@ -49,10 +49,17 @@ class Controller(object):
|
|||
|
||||
class SampleController(Controller):
|
||||
|
||||
def _getAgents(self):
|
||||
return [AgentSpecification('sample01', 'sample')]
|
||||
jobNumber = 0
|
||||
|
||||
controllers.register(SampleController, Master, name='sample')
|
||||
def _getAgents(self):
|
||||
return [AgentSpecification('sample01', 'base.sample')]
|
||||
|
||||
def enterJob(self, jobType, agent):
|
||||
self.jobNumber += 1
|
||||
spec = JobSpecification(jobType, '%05i' % self.jobNumber, agent=agent)
|
||||
self.agent.setupJobs([spec])
|
||||
|
||||
controllers.register(SampleController, Master, name='base.sample')
|
||||
|
||||
|
||||
class AgentSpecification(object):
|
||||
|
|
|
@ -46,7 +46,7 @@ class Job(object):
|
|||
|
||||
def execute(self):
|
||||
if self.agent is not None:
|
||||
self.agent.execute(self, self.params)
|
||||
self.agent.send(self)
|
||||
|
||||
def reschedule(self, startTime=None):
|
||||
self.scheduler.schedule(self.copy(), startTime)
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
# $Id$
|
||||
#
|
||||
|
||||
controller(name='sample')
|
||||
controller(name='base.sample')
|
||||
scheduler(name='sample')
|
||||
logger(name='default', standard=30)
|
||||
|
|
80
agent/core/agent.py
Normal file
80
agent/core/agent.py
Normal file
|
@ -0,0 +1,80 @@
|
|||
#
|
||||
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# This program 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 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program 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 this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
"""
|
||||
Queueable agent base/sample classes.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from twisted.internet.defer import succeed
|
||||
from zope.interface import implements
|
||||
|
||||
from cybertools.agent.base.agent import Agent, Master
|
||||
from cybertools.agent.components import agents
|
||||
from cybertools.agent.interfaces import IQueueableAgent
|
||||
|
||||
|
||||
class QueueableAgent(Agent):
|
||||
|
||||
implements(IQueueableAgent)
|
||||
|
||||
currentJob = None
|
||||
|
||||
def __init__(self, master):
|
||||
super(QueueableAgent, self).__init__(master)
|
||||
self.queue = []
|
||||
|
||||
def send(self, job):
|
||||
if self.currentJob is None:
|
||||
if self.queue: # this should not happen...
|
||||
self.queue.insert(0, job)
|
||||
job = self.queue.pop()
|
||||
self.execute(job)
|
||||
else:
|
||||
self.queue.insert(0, job)
|
||||
|
||||
def execute(self, job):
|
||||
self.currentJob = job
|
||||
d = self.process()
|
||||
d.addCallbacks(self.completed, self.error)
|
||||
|
||||
def process(self):
|
||||
# do something with the current job, return a deferred
|
||||
print ('Job %s on agent %s has been executed.'
|
||||
% (self.currentJob.identifier, self.name))
|
||||
return succeed('Done')
|
||||
|
||||
def completed(self, result):
|
||||
self.log(self.currentJob)
|
||||
# TODO: inform the master about the result of the job execution
|
||||
self.finishJob()
|
||||
|
||||
def error(self, result):
|
||||
print '*** error', result
|
||||
self.log(self.currentJob, result='Error')
|
||||
# TODO: inform the master about the result of the job execution
|
||||
self.finishJob()
|
||||
|
||||
def finishJob(self):
|
||||
self.currentJob = None
|
||||
if self.queue:
|
||||
job = self.queue.pop()
|
||||
self.execute(job, job.params)
|
||||
|
||||
agents.register(QueueableAgent, Master, name='core.sample')
|
37
agent/core/control.py
Normal file
37
agent/core/control.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
#
|
||||
# Copyright (c) 2008 Helmut Merz helmutm@cy55.de
|
||||
#
|
||||
# This program 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 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program 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 this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
|
||||
"""
|
||||
Base/sample controller implementation.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
||||
from zope.interface import implements
|
||||
|
||||
from cybertools.agent.base.agent import Master
|
||||
from cybertools.agent.base.control import SampleController, AgentSpecification
|
||||
from cybertools.agent.components import controllers
|
||||
|
||||
|
||||
class SampleController(SampleController):
|
||||
|
||||
def _getAgents(self):
|
||||
return [AgentSpecification('sample01', 'core.sample')]
|
||||
|
||||
controllers.register(SampleController, Master, name='core.sample')
|
|
@ -17,7 +17,7 @@
|
|||
#
|
||||
|
||||
"""
|
||||
Basic (sample) job scheduler.
|
||||
Basic job scheduler using twisted.
|
||||
|
||||
$Id$
|
||||
"""
|
||||
|
|
|
@ -36,11 +36,28 @@ class IAgent(Interface):
|
|||
config = Attribute('Configuration settings.')
|
||||
logger = Attribute('Logger instance to be used for recording '
|
||||
'job execution and execution results.')
|
||||
children = Attribute('A collection of agents that are managed by this '
|
||||
'master.')
|
||||
|
||||
def execute(job, params=None):
|
||||
""" Execute a job.
|
||||
def send(job):
|
||||
""" If the agent supports queueing and the agent is busy put
|
||||
job in queue, otherwise just execute the job.
|
||||
"""
|
||||
|
||||
def execute(job):
|
||||
""" Execute a job using the parameters given.
|
||||
"""
|
||||
|
||||
|
||||
class IQueueableAgent(Interface):
|
||||
""" An agent that keeps a queue of jobs. A queueable agent
|
||||
executes not more than one job at a time; when a running
|
||||
job is finished the next one will be taken from the queue.
|
||||
"""
|
||||
|
||||
queue = Attribute('A sequence of jobs to execute.')
|
||||
|
||||
def process():
|
||||
""" Do the real work asynchronously, returning a deferred.
|
||||
This method will be called by execute().
|
||||
"""
|
||||
|
||||
|
||||
|
@ -51,6 +68,8 @@ class IMaster(IAgent):
|
|||
config = Attribute('Central configuration settings.')
|
||||
controllers = Attribute('Collection of IController instances.')
|
||||
scheduler = Attribute('IScheduler instance.')
|
||||
children = Attribute('A collection of agents that are managed by this '
|
||||
'master.')
|
||||
|
||||
def setup():
|
||||
""" Set up the master agent by triggering all assigned controllers.
|
||||
|
|
Loading…
Add table
Reference in a new issue