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() |   >>> configFile.close() | ||||||
| 
 | 
 | ||||||
|   >>> master.config |   >>> master.config | ||||||
|   controller.name = 'sample' |   controller.name = 'base.sample' | ||||||
|   logger.name = 'default' |   logger.name = 'default' | ||||||
|   logger.standard = 30 |   logger.standard = 30 | ||||||
|   scheduler.name = 'sample' |   scheduler.name = 'sample' | ||||||
|  | @ -155,12 +155,10 @@ is just one scheduler associated with the master agent. | ||||||
|   >>> master.scheduler |   >>> master.scheduler | ||||||
|   <cybertools.agent.base.schedule.Scheduler object ...> |   <cybertools.agent.base.schedule.Scheduler object ...> | ||||||
| 
 | 
 | ||||||
| We schedule a sample job by taking the role of the controller and simply | We schedule a sample job by calling an internal method of the agent's | ||||||
| call the master agent's callback method for entering jobs. | controller. | ||||||
| 
 | 
 | ||||||
|   >>> from cybertools.agent.base.control import JobSpecification |   >>> master.controllers[0].enterJob('sample', 'sample01') | ||||||
|   >>> jobSpec = JobSpecification('sample', '00001', agent='sample01') |  | ||||||
|   >>> master.setupJobs([jobSpec]) |  | ||||||
|   Job 00001 on agent sample01 has been executed. |   Job 00001 on agent sample01 has been executed. | ||||||
| 
 | 
 | ||||||
| Logging | Logging | ||||||
|  | @ -173,15 +171,14 @@ Logging | ||||||
| 
 | 
 | ||||||
|   >>> master.config.logger.standard = 20 |   >>> master.config.logger.standard = 20 | ||||||
|   >>> master.logger.setup() |   >>> master.logger.setup() | ||||||
|   >>> jobSpec = JobSpecification('sample', '00002', agent='sample01') |   >>> master.controllers[0].enterJob('sample', 'sample01') | ||||||
|   >>> master.setupJobs([jobSpec]) |  | ||||||
|   Job 00002 on agent sample01 has been executed. |   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: |   >>> for r in master.logger.records: | ||||||
|   ...     print r |   ...     print r | ||||||
|   2... agent:sample01 job:00001 message:job execution |   2... agent:sample01 job:00001 message:job execution result:OK | ||||||
|   2... agent:sample01 job:00002 message:job execution |   2... agent:sample01 job:00002 message:job execution result:OK | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| Using the Twisted-based Scheduler | 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 | By specifying the core scheduler in the agent's configuration this will be | ||||||
| used automatically for scheduling. | 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 = ''' |   >>> config = ''' | ||||||
|   ... controller(name='sample') |   ... controller(name='core.sample') | ||||||
|   ... scheduler(name='core') |   ... scheduler(name='core') | ||||||
|   ... logger(name='default', standard=30) |   ... logger(name='default', standard=30) | ||||||
|   ... ''' |   ... ''' | ||||||
|   >>> master = Master(config) |   >>> master = Master(config) | ||||||
|  |   >>> master.setup() | ||||||
| 
 | 
 | ||||||
|   >>> master.scheduler |   >>> master.scheduler | ||||||
|   <cybertools.agent.core.schedule.Scheduler object ...> |   <cybertools.agent.core.schedule.Scheduler object ...> | ||||||
| 
 | 
 | ||||||
| We trigger the controller's setup as above and enter the same | We enter the same job specification as above. | ||||||
| job specification. |  | ||||||
| 
 | 
 | ||||||
|   >>> master.setup() |   >>> master.controllers[0].enterJob('sample', 'sample01') | ||||||
| 
 |  | ||||||
|   >>> jobSpec = JobSpecification('sample', '00001', agent='sample01') |  | ||||||
|   >>> master.setupJobs([jobSpec]) |  | ||||||
| 
 | 
 | ||||||
| Now the job is not executed immediately - we have to hand over control to | 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 | 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 |   >>> from cybertools.agent.tests import tester | ||||||
|   >>> tester.iterate() |   >>> tester.iterate() | ||||||
|  |  | ||||||
|  | @ -5,4 +5,4 @@ $Id$ | ||||||
| # register default adapters | # register default adapters | ||||||
| 
 | 
 | ||||||
| from cybertools.agent.base import agent, control, job, log, schedule | 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): |     def execute(self, job, params=None): | ||||||
|         pass |         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): | class Master(Agent): | ||||||
| 
 | 
 | ||||||
|  | @ -84,12 +88,11 @@ class Master(Agent): | ||||||
| 
 | 
 | ||||||
| class SampleAgent(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) |         print 'Job %s on agent %s has been executed.' % (job.identifier, self.name) | ||||||
|         self.log(job) |         self.log(job) | ||||||
| 
 | 
 | ||||||
|     def log(self, job): | agents.register(SampleAgent, Master, name='base.sample') | ||||||
|         self.logger.log(dict(message='job execution', job=job.identifier, |  | ||||||
|                         agent=self.name)) |  | ||||||
| 
 |  | ||||||
| agents.register(SampleAgent, Master, name='sample') |  | ||||||
|  |  | ||||||
|  | @ -49,10 +49,17 @@ class Controller(object): | ||||||
| 
 | 
 | ||||||
| class SampleController(Controller): | class SampleController(Controller): | ||||||
| 
 | 
 | ||||||
|     def _getAgents(self): |     jobNumber = 0 | ||||||
|         return [AgentSpecification('sample01', 'sample')] |  | ||||||
| 
 | 
 | ||||||
| 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): | class AgentSpecification(object): | ||||||
|  |  | ||||||
|  | @ -46,7 +46,7 @@ class Job(object): | ||||||
| 
 | 
 | ||||||
|     def execute(self): |     def execute(self): | ||||||
|         if self.agent is not None: |         if self.agent is not None: | ||||||
|             self.agent.execute(self, self.params) |             self.agent.send(self) | ||||||
| 
 | 
 | ||||||
|     def reschedule(self, startTime=None): |     def reschedule(self, startTime=None): | ||||||
|         self.scheduler.schedule(self.copy(), startTime) |         self.scheduler.schedule(self.copy(), startTime) | ||||||
|  |  | ||||||
|  | @ -4,6 +4,6 @@ | ||||||
| #   $Id$ | #   $Id$ | ||||||
| # | # | ||||||
| 
 | 
 | ||||||
| controller(name='sample') | controller(name='base.sample') | ||||||
| scheduler(name='sample') | scheduler(name='sample') | ||||||
| logger(name='default', standard=30) | 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$ | $Id$ | ||||||
| """ | """ | ||||||
|  |  | ||||||
|  | @ -36,11 +36,28 @@ class IAgent(Interface): | ||||||
|     config = Attribute('Configuration settings.') |     config = Attribute('Configuration settings.') | ||||||
|     logger = Attribute('Logger instance to be used for recording ' |     logger = Attribute('Logger instance to be used for recording ' | ||||||
|                     'job execution and execution results.') |                     'job execution and execution results.') | ||||||
|     children = Attribute('A collection of agents that are managed by this ' |  | ||||||
|                     'master.') |  | ||||||
| 
 | 
 | ||||||
|     def execute(job, params=None): |     def send(job): | ||||||
|         """ Execute a 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.') |     config = Attribute('Central configuration settings.') | ||||||
|     controllers = Attribute('Collection of IController instances.') |     controllers = Attribute('Collection of IController instances.') | ||||||
|     scheduler = Attribute('IScheduler instance.') |     scheduler = Attribute('IScheduler instance.') | ||||||
|  |     children = Attribute('A collection of agents that are managed by this ' | ||||||
|  |                     'master.') | ||||||
| 
 | 
 | ||||||
|     def setup(): |     def setup(): | ||||||
|         """ Set up the master agent by triggering all assigned controllers. |         """ Set up the master agent by triggering all assigned controllers. | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 helmutm
						helmutm