Here is a patch, which extends the autotest system with recurring job
executions. When you create a new recurring job, you can specify:
- start time (on server)
- loop count (0 means infinite): how many times it will executed
- loop period: how many time will wait between two execution

Added features:
- Create job: can create Template job.
- View job: added "Create recurring job"
- New tab "Recurring job"
  - list of recurring jobs
     - can click on it to view the executed job
     - selection support
     - Action -> remove
  - creation  panel (can be accessible thru "Create recurring job")
     - submit/create
     - reset
     - data validity check

From: Zoltan Sogor <weth@inf.u-szeged.hu>
Signed-off-by: Steve Howard <showard@google.com>



git-svn-id: http://test.kernel.org/svn/autotest/trunk@3064 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/scheduler/monitor_db.py b/scheduler/monitor_db.py
index e299d5a..93718fe 100755
--- a/scheduler/monitor_db.py
+++ b/scheduler/monitor_db.py
@@ -14,7 +14,7 @@
 from autotest_lib.client.common_lib import global_config
 from autotest_lib.client.common_lib import host_protections, utils
 from autotest_lib.database import database_connection
-from autotest_lib.frontend.afe import models
+from autotest_lib.frontend.afe import models, rpc_utils
 from autotest_lib.scheduler import drone_manager, drones, email_manager
 from autotest_lib.scheduler import monitor_db_cleanup
 from autotest_lib.scheduler import status_server, scheduler_config
@@ -602,6 +602,7 @@
         _drone_manager.refresh()
         self._run_cleanup()
         self._find_aborting()
+        self._process_recurring_runs()
         self._schedule_new_jobs()
         self._handle_agents()
         _drone_manager.execute_actions()
@@ -938,6 +939,57 @@
                      _drone_manager.total_running_processes())
 
 
+    def _process_recurring_runs(self):
+        recurring_runs = models.RecurringRun.objects.filter(
+            start_date__lte=datetime.datetime.now())
+        for rrun in recurring_runs:
+            # Create job from template
+            job = rrun.job
+            info = rpc_utils.get_job_info(job)
+
+            host_objects = info['hosts']
+            one_time_hosts = info['one_time_hosts']
+            metahost_objects = info['meta_hosts']
+            dependencies = info['dependencies']
+            atomic_group = info['atomic_group']
+
+            for host in one_time_hosts or []:
+                this_host = models.Host.create_one_time_host(host.hostname)
+                host_objects.append(this_host)
+
+            try:
+                rpc_utils.create_new_job(owner=rrun.owner.login,
+                                         host_objects=host_objects,
+                                         metahost_objects=metahost_objects,
+                                         name=job.name,
+                                         priority=job.priority,
+                                         control_file=job.control_file,
+                                         control_type=job.control_type,
+                                         is_template=False,
+                                         synch_count=job.synch_count,
+                                         timeout=job.timeout,
+                                         run_verify=job.run_verify,
+                                         email_list=job.email_list,
+                                         dependencies=dependencies,
+                                         reboot_before=job.reboot_before,
+                                         reboot_after=job.reboot_after,
+                                         atomic_group=atomic_group)
+
+            except Exception, ex:
+                logging.exception(ex)
+                #TODO send email
+
+            if rrun.loop_count == 1:
+                rrun.delete()
+            else:
+                if rrun.loop_count != 0: # if not infinite loop
+                    # calculate new start_date
+                    difference = datetime.timedelta(seconds=rrun.loop_period)
+                    rrun.start_date = rrun.start_date + difference
+                    rrun.loop_count -= 1
+                    rrun.save()
+
+
 class PidfileRunMonitor(object):
     """
     Client must call either run() to start a new process or