Implement sync_count.  The primary change here is replacing the job.synch_type field with a synch_count field.  There is no longer just a distinction between synchronous and asynchronous jobs.  Instead, every job as a synch_count, with synch_count = 1 corresponding to the old concept of synchronous jobs.  This required:
-changes to the job creation RPC and corresponding client code in AFE and the CLI
-massive changes to the scheduler to schedule all jobs in groups based on synch_count (this unified the old synch and async code paths)
-changed results directory structure to accomodate synchronous groups, as documented at http://autotest.kernel.org/wiki/SchedulerSpecification, including widespread changes to monitor_db and a change in AFE
-changes to AFE abort code to handle synchronous groups instead of just synchronous jobs
-also got rid of the "synchronizing" field in the jobs table, since I was changing the table anyway and it seems very likely now that that field will never be used

other changes included:
-add some logging to afe/models.py to match what the scheduler code does, since the scheduler is starting to use the models more
-added checks for aborts of synchronous groups to abort_host_queue_entries RPC



git-svn-id: http://test.kernel.org/svn/autotest/trunk@2402 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/frontend/afe/rpc_utils.py b/frontend/afe/rpc_utils.py
index fcdc0ba..0e25d05 100644
--- a/frontend/afe/rpc_utils.py
+++ b/frontend/afe/rpc_utils.py
@@ -128,26 +128,17 @@
              'tests together (tests %s and %s differ' % (
             test1.name, test2.name)})
 
-    try:
-        synch_type = get_consistent_value(test_objects, 'synch_type')
-    except InconsistencyException, exc:
-        test1, test2 = exc.args
-        raise model_logic.ValidationError(
-            {'tests' : 'You cannot run both synchronous and '
-             'asynchronous tests together (tests %s and %s differ)' % (
-            test1.name, test2.name)})
-
     is_server = (test_type == models.Test.Types.SERVER)
-    is_synchronous = (synch_type == models.Test.SynchType.SYNCHRONOUS)
+    synch_count = max(test.sync_count for test in test_objects)
     if label:
         label = models.Label.smart_get(label)
 
     dependencies = set(label.name for label
                        in models.Label.objects.filter(test__in=test_objects))
 
-    return (dict(is_server=is_server, is_synchronous=is_synchronous,
-                 dependencies=list(dependencies)),
-            test_objects, profiler_objects, label)
+    cf_info = dict(is_server=is_server, synch_count=synch_count,
+                   dependencies=list(dependencies))
+    return cf_info, test_objects, profiler_objects, label
 
 
 def check_job_dependencies(host_objects, job_dependencies):
@@ -183,3 +174,27 @@
                        ', '.join(host.hostname for host in hosts_in_label)))
     if errors:
         raise model_logic.ValidationError({'hosts' : '\n'.join(errors)})
+
+
+def _execution_key_for(host_queue_entry):
+    return (host_queue_entry.job.id, host_queue_entry.execution_subdir)
+
+
+def check_abort_synchronous_jobs(host_queue_entries):
+    # ensure user isn't aborting part of a synchronous autoserv execution
+    count_per_execution = {}
+    for queue_entry in host_queue_entries:
+        key = _execution_key_for(queue_entry)
+        count_per_execution.setdefault(key, 0)
+        count_per_execution[key] += 1
+
+    for queue_entry in host_queue_entries:
+        if not queue_entry.execution_subdir:
+            continue
+        execution_count = count_per_execution[_execution_key_for(queue_entry)]
+        if execution_count < queue_entry.job.synch_count:
+          raise model_logic.ValidationError(
+              {'' : 'You cannot abort part of a synchronous job execution '
+                    '(%d/%s, %d included, %d expected'
+                    % (queue_entry.job.id, queue_entry.execution_subdir,
+                       execution_subdir, queue_entry.job.synch_count)})