blob: e0c2a6e1b966a6bb3bf4e87618cf6026379dca5d [file] [log] [blame]
mblighe8819cd2008-02-15 16:48:40 +00001"""\
2Utility functions for rpc_interface.py. We keep them in a separate file so that
3only RPC interface functions go into that file.
4"""
5
6__author__ = 'showard@google.com (Steve Howard)'
7
8import datetime, xmlrpclib, threading
mblighec5546d2008-06-16 16:51:28 +00009from frontend.afe import models, model_logic
mblighe8819cd2008-02-15 16:48:40 +000010
11def prepare_for_serialization(objects):
jadmanski0afbb632008-06-06 21:10:57 +000012 """
13 Prepare Python objects to be returned via RPC.
14 """
15 if (isinstance(objects, list) and len(objects) and
16 isinstance(objects[0], dict) and 'id' in objects[0]):
17 objects = gather_unique_dicts(objects)
18 return _prepare_data(objects)
showardb8d34242008-04-25 18:11:16 +000019
20
21def _prepare_data(data):
jadmanski0afbb632008-06-06 21:10:57 +000022 """
23 Recursively process data structures, performing necessary type
24 conversions to values in data to allow for RPC serialization:
25 -convert datetimes to strings
showard2b9a88b2008-06-13 20:55:03 +000026 -convert tuples and sets to lists
jadmanski0afbb632008-06-06 21:10:57 +000027 """
28 if isinstance(data, dict):
29 new_data = {}
30 for key, value in data.iteritems():
31 new_data[key] = _prepare_data(value)
32 return new_data
showard2b9a88b2008-06-13 20:55:03 +000033 elif (isinstance(data, list) or isinstance(data, tuple) or
34 isinstance(data, set)):
jadmanski0afbb632008-06-06 21:10:57 +000035 return [_prepare_data(item) for item in data]
showard98659972008-07-17 17:00:07 +000036 elif isinstance(data, datetime.date):
jadmanski0afbb632008-06-06 21:10:57 +000037 return str(data)
38 else:
39 return data
mblighe8819cd2008-02-15 16:48:40 +000040
41
showardb0dfb9f2008-06-06 18:08:02 +000042def gather_unique_dicts(dict_iterable):
jadmanski0afbb632008-06-06 21:10:57 +000043 """\
44 Pick out unique objects (by ID) from an iterable of object dicts.
45 """
46 id_set = set()
47 result = []
48 for obj in dict_iterable:
49 if obj['id'] not in id_set:
50 id_set.add(obj['id'])
51 result.append(obj)
52 return result
showardb0dfb9f2008-06-06 18:08:02 +000053
54
mblighe8819cd2008-02-15 16:48:40 +000055def extra_job_filters(not_yet_run=False, running=False, finished=False):
jadmanski0afbb632008-06-06 21:10:57 +000056 """\
57 Generate a SQL WHERE clause for job status filtering, and return it in
58 a dict of keyword args to pass to query.extra(). No more than one of
59 the parameters should be passed as True.
60 """
61 assert not ((not_yet_run and running) or
62 (not_yet_run and finished) or
63 (running and finished)), ('Cannot specify more than one '
64 'filter to this function')
65 if not_yet_run:
66 where = ['id NOT IN (SELECT job_id FROM host_queue_entries '
67 'WHERE active OR complete)']
68 elif running:
69 where = ['(id IN (SELECT job_id FROM host_queue_entries '
70 'WHERE active OR complete)) AND '
71 '(id IN (SELECT job_id FROM host_queue_entries '
72 'WHERE not complete OR active))']
73 elif finished:
74 where = ['id NOT IN (SELECT job_id FROM host_queue_entries '
75 'WHERE not complete OR active)']
76 else:
77 return None
78 return {'where': where}
mblighe8819cd2008-02-15 16:48:40 +000079
80
showard8e3aa5e2008-04-08 19:42:32 +000081def extra_host_filters(multiple_labels=[]):
jadmanski0afbb632008-06-06 21:10:57 +000082 """\
83 Generate SQL WHERE clauses for matching hosts in an intersection of
84 labels.
85 """
86 extra_args = {}
87 where_str = ('hosts.id in (select host_id from hosts_labels '
88 'where label_id=%s)')
89 extra_args['where'] = [where_str] * len(multiple_labels)
90 extra_args['params'] = [models.Label.smart_get(label).id
91 for label in multiple_labels]
92 return extra_args
showard8e3aa5e2008-04-08 19:42:32 +000093
94
showard8fd58242008-03-10 21:29:07 +000095class InconsistencyException(Exception):
jadmanski0afbb632008-06-06 21:10:57 +000096 'Raised when a list of objects does not have a consistent value'
showard8fd58242008-03-10 21:29:07 +000097
98
99def get_consistent_value(objects, field):
jadmanski0afbb632008-06-06 21:10:57 +0000100 value = getattr(objects[0], field)
101 for obj in objects:
102 this_value = getattr(obj, field)
103 if this_value != value:
104 raise InconsistencyException(objects[0], obj)
105 return value
showard8fd58242008-03-10 21:29:07 +0000106
107
showard2b9a88b2008-06-13 20:55:03 +0000108def prepare_generate_control_file(tests, kernel, label, profilers):
jadmanski0afbb632008-06-06 21:10:57 +0000109 test_objects = [models.Test.smart_get(test) for test in tests]
showard2b9a88b2008-06-13 20:55:03 +0000110 profiler_objects = [models.Profiler.smart_get(profiler)
111 for profiler in profilers]
jadmanski0afbb632008-06-06 21:10:57 +0000112 # ensure tests are all the same type
113 try:
114 test_type = get_consistent_value(test_objects, 'test_type')
115 except InconsistencyException, exc:
116 test1, test2 = exc.args
mblighec5546d2008-06-16 16:51:28 +0000117 raise model_logic.ValidationError(
jadmanski0afbb632008-06-06 21:10:57 +0000118 {'tests' : 'You cannot run both server- and client-side '
119 'tests together (tests %s and %s differ' % (
120 test1.name, test2.name)})
showard8fd58242008-03-10 21:29:07 +0000121
jadmanski0afbb632008-06-06 21:10:57 +0000122 try:
123 synch_type = get_consistent_value(test_objects, 'synch_type')
124 except InconsistencyException, exc:
125 test1, test2 = exc.args
mblighec5546d2008-06-16 16:51:28 +0000126 raise model_logic.ValidationError(
jadmanski0afbb632008-06-06 21:10:57 +0000127 {'tests' : 'You cannot run both synchronous and '
128 'asynchronous tests together (tests %s and %s differ)' % (
129 test1.name, test2.name)})
mblighe8819cd2008-02-15 16:48:40 +0000130
jadmanski0afbb632008-06-06 21:10:57 +0000131 is_server = (test_type == models.Test.Types.SERVER)
132 is_synchronous = (synch_type == models.Test.SynchType.SYNCHRONOUS)
133 if label:
134 label = models.Label.smart_get(label)
mblighe8819cd2008-02-15 16:48:40 +0000135
showard2b9a88b2008-06-13 20:55:03 +0000136 return is_server, is_synchronous, test_objects, profiler_objects, label