Add support for atomic groups to the frontend RPC interface.
Signed-off-by: Gregory Smith <gps@google.com>
git-svn-id: http://test.kernel.org/svn/autotest/trunk@2967 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/frontend/afe/rpc_utils.py b/frontend/afe/rpc_utils.py
index 23a51f7..f08eb99 100644
--- a/frontend/afe/rpc_utils.py
+++ b/frontend/afe/rpc_utils.py
@@ -21,6 +21,28 @@
return _prepare_data(objects)
+def prepare_rows_as_nested_dicts(query, nested_dict_column_names):
+ """
+ Prepare a Django query to be returned via RPC as a sequence of nested
+ dictionaries.
+
+ @param query - A Django model query object with a select_related() method.
+ @param nested_dict_column_names - A list of column/attribute names for the
+ rows returned by query to expand into nested dictionaries using
+ their get_object_dict() method when not None.
+
+ @returns An list suitable to returned in an RPC.
+ """
+ all_dicts = []
+ for row in query.select_related():
+ row_dict = row.get_object_dict()
+ for column in nested_dict_column_names:
+ if row_dict[column] is not None:
+ row_dict[column] = getattr(row, column).get_object_dict()
+ all_dicts.append(row_dict)
+ return prepare_for_serialization(all_dicts)
+
+
def _prepare_data(data):
"""
Recursively process data structures, performing necessary type
@@ -201,11 +223,77 @@
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'
+ '(%d/%s), %d included, %d expected'
% (queue_entry.job.id, queue_entry.execution_subdir,
execution_count, queue_entry.job.synch_count)})
+def check_atomic_group_create_job(synch_count, host_objects, metahost_objects,
+ dependencies, atomic_group, labels_by_name):
+ """
+ Attempt to reject create_job requests with an atomic group that
+ will be impossible to schedule. The checks are not perfect but
+ should catch the most obvious issues.
+
+ @param synch_count - The job's minimum synch count.
+ @param host_objects - A list of models.Host instances.
+ @param metahost_objects - A list of models.Label instances.
+ @param dependencies - A list of job dependency label names.
+ @param atomic_group - The models.AtomicGroup instance.
+ @param labels_by_name - A dictionary mapping label names to models.Label
+ instance. Used to look up instances for dependencies.
+
+ @raises model_logic.ValidationError - When an issue is found.
+ """
+ # If specific host objects were supplied with an atomic group, verify
+ # that there are enough to satisfy the synch_count.
+ minimum_required = synch_count or 1
+ if (host_objects and not metahost_objects and
+ len(host_objects) < minimum_required):
+ raise model_logic.ValidationError(
+ {'hosts':
+ 'only %d hosts provided for job with synch_count = %d' %
+ (len(host_objects), synch_count)})
+
+ # Check that the atomic group has a hope of running this job
+ # given any supplied metahosts and dependancies that may limit.
+
+ # Get a set of hostnames in the atomic group.
+ possible_hosts = set()
+ for label in atomic_group.label_set.all():
+ possible_hosts.update(h.hostname for h in label.host_set.all())
+
+ # Filter out hosts that don't match all of the job dependency labels.
+ for label_name in set(dependencies):
+ label = labels_by_name[label_name]
+ hosts_in_label = (h.hostname for h in label.host_set.all())
+ possible_hosts.intersection_update(hosts_in_label)
+
+ host_set = set(host.hostname for host in host_objects)
+ unusable_host_set = host_set.difference(possible_hosts)
+ if unusable_host_set:
+ raise model_logic.ValidationError(
+ {'hosts': 'Hosts "%s" are not in Atomic Group "%s"' %
+ (', '.join(sorted(unusable_host_set)), atomic_group.name)})
+
+ # Lookup hosts provided by each meta host and merge them into the
+ # host_set for final counting.
+ for meta_host in metahost_objects:
+ meta_possible = possible_hosts.copy()
+ hosts_in_meta_host = (h.hostname for h in meta_host.host_set.all())
+ meta_possible.intersection_update(hosts_in_meta_host)
+
+ # Count all hosts that this meta_host will provide.
+ host_set.update(meta_possible)
+
+ if len(host_set) < minimum_required:
+ raise model_logic.ValidationError(
+ {'atomic_group_name':
+ 'Insufficient hosts in Atomic Group "%s" with the'
+ ' supplied dependencies and meta_hosts.' %
+ (atomic_group.name,)})
+
+
def get_motd():
dirname = os.path.dirname(__file__)
filename = os.path.join(dirname, "..", "..", "motd.txt")