backend support for hostless jobs
* support in rpc_interface.create_job() and models for creating a hostless job -- a job with one queue entry with no host, meta_host or atomic_group
* support in scheduler for recognizing and executing such a job.  the bulk of the work was in extracting an AbstractQueueTask class from QueueTask, containing all the logic not pertaining to hosts.  I then added a simple HostlessQueueTask class also inheriting from it.
Also got rid of HostQueueEntry.get_host() and added an extra log line when AgentTasks finish (used to be for QueueTasks only).

Signed-off-by: Steve Howard <showard@google.com>


git-svn-id: http://test.kernel.org/svn/autotest/trunk@4018 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/scheduler/monitor_db_functional_test.py b/scheduler/monitor_db_functional_test.py
index 3b60931..228027f 100644
--- a/scheduler/monitor_db_functional_test.py
+++ b/scheduler/monitor_db_functional_test.py
@@ -220,6 +220,7 @@
     def execute_command(self, command, working_directory, pidfile_name,
                         num_processes, log_file=None, paired_with_pidfile=None,
                         username=None):
+        logging.debug('Executing %s in %s', command, working_directory)
         pidfile_id = self._DummyPidfileId(working_directory, pidfile_name)
         if pidfile_id.key() in self._pidfile_index:
             pidfile_id = self._pidfile_index[pidfile_id.key()]
@@ -235,7 +236,7 @@
 
     def get_pidfile_contents(self, pidfile_id, use_second_read=False):
         if pidfile_id not in self._pidfiles:
-            print 'Request for nonexistent pidfile %s' % pidfile_id
+            logging.debug('Request for nonexistent pidfile %s' % pidfile_id)
         return self._pidfiles.get(pidfile_id, drone_manager.PidfileContents())
 
 
@@ -359,16 +360,20 @@
 
     def _check_statuses(self, queue_entry, queue_entry_status,
                         host_status=None):
+        self._check_entry_status(queue_entry, queue_entry_status)
+        if host_status:
+            self._check_host_status(queue_entry.host, host_status)
+
+
+    def _check_entry_status(self, queue_entry, status):
         # update from DB
         queue_entry = self._update_instance(queue_entry)
-        self.assertEquals(queue_entry.status, queue_entry_status)
-        if host_status:
-            self.assertEquals(queue_entry.host.status, host_status)
+        self.assertEquals(queue_entry.status, status)
 
 
     def _check_host_status(self, host, status):
         # update from DB
-        host = models.Host.objects.get(id=host.id)
+        host = self._update_instance(host)
         self.assertEquals(host.status, status)
 
 
@@ -964,5 +969,17 @@
         self._check_statuses(entry, HqeStatus.RUNNING, HostStatus.RUNNING)
 
 
+    def test_hostless_job(self):
+        job = self._create_job(hostless=True)
+        entry = job.hostqueueentry_set.all()[0]
+
+        self._run_dispatcher()
+        self._check_entry_status(entry, HqeStatus.RUNNING)
+
+        self.mock_drone_manager.finish_process(_PidfileType.JOB)
+        self._run_dispatcher()
+        self._check_entry_status(entry, HqeStatus.COMPLETED)
+
+
 if __name__ == '__main__':
     unittest.main()