blob: 73b4a58e03d3209e708f4e82ddb0a08e60a04ddc [file] [log] [blame]
Aviv Keshet0b9cfc92013-02-05 11:36:02 -08001# pylint: disable-msg=C0111
2
mblighe8819cd2008-02-15 16:48:40 +00003"""\
4Functions to expose over the RPC interface.
5
6For all modify* and delete* functions that ask for an 'id' parameter to
7identify the object to operate on, the id may be either
8 * the database row ID
9 * the name of the object (label name, hostname, user login, etc.)
10 * a dictionary containing uniquely identifying field (this option should seldom
11 be used)
12
13When specifying foreign key fields (i.e. adding hosts to a label, or adding
14users to an ACL group), the given value may be either the database row ID or the
15name of the object.
16
17All get* functions return lists of dictionaries. Each dictionary represents one
18object and maps field names to values.
19
20Some examples:
21modify_host(2, hostname='myhost') # modify hostname of host with database ID 2
22modify_host('ipaj2', hostname='myhost') # modify hostname of host 'ipaj2'
23modify_test('sleeptest', test_type='Client', params=', seconds=60')
24delete_acl_group(1) # delete by ID
25delete_acl_group('Everyone') # delete by name
26acl_group_add_users('Everyone', ['mbligh', 'showard'])
27get_jobs(owner='showard', status='Queued')
28
mbligh93c80e62009-02-03 17:48:30 +000029See doctests/001_rpc_test.txt for (lots) more examples.
mblighe8819cd2008-02-15 16:48:40 +000030"""
31
32__author__ = 'showard@google.com (Steve Howard)'
33
showard29f7cd22009-04-29 21:16:24 +000034import datetime
showardcafd16e2009-05-29 18:37:49 +000035import common
Alex Millera713e252013-03-01 10:45:44 -080036from autotest_lib.client.common_lib import error
jamesrendd855242010-03-02 22:23:44 +000037from autotest_lib.frontend.afe import models, model_logic, model_attributes
showard6d7b2ff2009-06-10 00:16:47 +000038from autotest_lib.frontend.afe import control_file, rpc_utils
showard3bb499f2008-07-03 19:42:20 +000039
mblighe8819cd2008-02-15 16:48:40 +000040
Eric Lid23bc192011-02-09 14:38:57 -080041def get_parameterized_autoupdate_image_url(job):
42 """Get the parameterized autoupdate image url from a parameterized job."""
43 known_test_obj = models.Test.smart_get('autoupdate_ParameterizedJob')
44 image_parameter = known_test_obj.testparameter_set.get(test=known_test_obj,
45 name='image')
46 para_set = job.parameterized_job.parameterizedjobparameter_set
47 job_test_para = para_set.get(test_parameter=image_parameter)
48 return job_test_para.parameter_value
49
50
mblighe8819cd2008-02-15 16:48:40 +000051# labels
52
showard989f25d2008-10-01 11:38:11 +000053def add_label(name, kernel_config=None, platform=None, only_if_needed=None):
showardc92da832009-04-07 18:14:34 +000054 return models.Label.add_object(
55 name=name, kernel_config=kernel_config, platform=platform,
56 only_if_needed=only_if_needed).id
mblighe8819cd2008-02-15 16:48:40 +000057
58
59def modify_label(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +000060 models.Label.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +000061
62
63def delete_label(id):
jadmanski0afbb632008-06-06 21:10:57 +000064 models.Label.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +000065
66
showardbbabf502008-06-06 00:02:02 +000067def label_add_hosts(id, hosts):
showardbe3ec042008-11-12 18:16:07 +000068 host_objs = models.Host.smart_get_bulk(hosts)
showardcafd16e2009-05-29 18:37:49 +000069 label = models.Label.smart_get(id)
70 if label.platform:
71 models.Host.check_no_platform(host_objs)
72 label.host_set.add(*host_objs)
showardbbabf502008-06-06 00:02:02 +000073
74
75def label_remove_hosts(id, hosts):
showardbe3ec042008-11-12 18:16:07 +000076 host_objs = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +000077 models.Label.smart_get(id).host_set.remove(*host_objs)
showardbbabf502008-06-06 00:02:02 +000078
79
mblighe8819cd2008-02-15 16:48:40 +000080def get_labels(**filter_data):
showardc92da832009-04-07 18:14:34 +000081 """\
82 @returns A sequence of nested dictionaries of label information.
83 """
84 return rpc_utils.prepare_rows_as_nested_dicts(
85 models.Label.query_objects(filter_data),
86 ('atomic_group',))
87
88
89# atomic groups
90
showarde9450c92009-06-30 01:58:52 +000091def add_atomic_group(name, max_number_of_machines=None, description=None):
showardc92da832009-04-07 18:14:34 +000092 return models.AtomicGroup.add_object(
93 name=name, max_number_of_machines=max_number_of_machines,
94 description=description).id
95
96
97def modify_atomic_group(id, **data):
98 models.AtomicGroup.smart_get(id).update_object(data)
99
100
101def delete_atomic_group(id):
102 models.AtomicGroup.smart_get(id).delete()
103
104
105def atomic_group_add_labels(id, labels):
106 label_objs = models.Label.smart_get_bulk(labels)
107 models.AtomicGroup.smart_get(id).label_set.add(*label_objs)
108
109
110def atomic_group_remove_labels(id, labels):
111 label_objs = models.Label.smart_get_bulk(labels)
112 models.AtomicGroup.smart_get(id).label_set.remove(*label_objs)
113
114
115def get_atomic_groups(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000116 return rpc_utils.prepare_for_serialization(
showardc92da832009-04-07 18:14:34 +0000117 models.AtomicGroup.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000118
119
120# hosts
121
showarddf062562008-07-03 19:56:37 +0000122def add_host(hostname, status=None, locked=None, protection=None):
jadmanski0afbb632008-06-06 21:10:57 +0000123 return models.Host.add_object(hostname=hostname, status=status,
showarddf062562008-07-03 19:56:37 +0000124 locked=locked, protection=protection).id
mblighe8819cd2008-02-15 16:48:40 +0000125
126
127def modify_host(id, **data):
showardbe0d8692009-08-20 23:42:44 +0000128 rpc_utils.check_modify_host(data)
showardce7c0922009-09-11 18:39:24 +0000129 host = models.Host.smart_get(id)
130 rpc_utils.check_modify_host_locking(host, data)
131 host.update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000132
133
showard276f9442009-05-20 00:33:16 +0000134def modify_hosts(host_filter_data, update_data):
135 """
showardbe0d8692009-08-20 23:42:44 +0000136 @param host_filter_data: Filters out which hosts to modify.
137 @param update_data: A dictionary with the changes to make to the hosts.
showard276f9442009-05-20 00:33:16 +0000138 """
showardbe0d8692009-08-20 23:42:44 +0000139 rpc_utils.check_modify_host(update_data)
showard276f9442009-05-20 00:33:16 +0000140 hosts = models.Host.query_objects(host_filter_data)
141 for host in hosts:
142 host.update_object(update_data)
143
144
mblighe8819cd2008-02-15 16:48:40 +0000145def host_add_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000146 labels = models.Label.smart_get_bulk(labels)
showardcafd16e2009-05-29 18:37:49 +0000147 host = models.Host.smart_get(id)
148
149 platforms = [label.name for label in labels if label.platform]
150 if len(platforms) > 1:
151 raise model_logic.ValidationError(
152 {'labels': 'Adding more than one platform label: %s' %
153 ', '.join(platforms)})
154 if len(platforms) == 1:
155 models.Host.check_no_platform([host])
156 host.labels.add(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000157
158
159def host_remove_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000160 labels = models.Label.smart_get_bulk(labels)
jadmanski0afbb632008-06-06 21:10:57 +0000161 models.Host.smart_get(id).labels.remove(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000162
163
showard0957a842009-05-11 19:25:08 +0000164def set_host_attribute(attribute, value, **host_filter_data):
165 """
166 @param attribute string name of attribute
167 @param value string, or None to delete an attribute
168 @param host_filter_data filter data to apply to Hosts to choose hosts to act
169 upon
170 """
171 assert host_filter_data # disallow accidental actions on all hosts
172 hosts = models.Host.query_objects(host_filter_data)
173 models.AclGroup.check_for_acl_violation_hosts(hosts)
174
175 for host in hosts:
showardf8b19042009-05-12 17:22:49 +0000176 host.set_or_delete_attribute(attribute, value)
showard0957a842009-05-11 19:25:08 +0000177
178
mblighe8819cd2008-02-15 16:48:40 +0000179def delete_host(id):
jadmanski0afbb632008-06-06 21:10:57 +0000180 models.Host.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000181
182
showard87cc38f2009-08-20 23:37:04 +0000183def get_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000184 exclude_atomic_group_hosts=False, valid_only=True, **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000185 """
186 @param multiple_labels: match hosts in all of the labels given. Should
187 be a list of label names.
188 @param exclude_only_if_needed_labels: Exclude hosts with at least one
189 "only_if_needed" label applied.
190 @param exclude_atomic_group_hosts: Exclude hosts that have one or more
191 atomic group labels associated with them.
jadmanski0afbb632008-06-06 21:10:57 +0000192 """
showard43a3d262008-11-12 18:17:05 +0000193 hosts = rpc_utils.get_host_query(multiple_labels,
194 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000195 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000196 valid_only, filter_data)
showard0957a842009-05-11 19:25:08 +0000197 hosts = list(hosts)
198 models.Host.objects.populate_relationships(hosts, models.Label,
199 'label_list')
200 models.Host.objects.populate_relationships(hosts, models.AclGroup,
201 'acl_list')
202 models.Host.objects.populate_relationships(hosts, models.HostAttribute,
203 'attribute_list')
showard43a3d262008-11-12 18:17:05 +0000204 host_dicts = []
205 for host_obj in hosts:
206 host_dict = host_obj.get_object_dict()
showard0957a842009-05-11 19:25:08 +0000207 host_dict['labels'] = [label.name for label in host_obj.label_list]
showard909c9142009-07-07 20:54:42 +0000208 host_dict['platform'], host_dict['atomic_group'] = (rpc_utils.
209 find_platform_and_atomic_group(host_obj))
showard0957a842009-05-11 19:25:08 +0000210 host_dict['acls'] = [acl.name for acl in host_obj.acl_list]
211 host_dict['attributes'] = dict((attribute.attribute, attribute.value)
212 for attribute in host_obj.attribute_list)
showard43a3d262008-11-12 18:17:05 +0000213 host_dicts.append(host_dict)
214 return rpc_utils.prepare_for_serialization(host_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000215
216
showard87cc38f2009-08-20 23:37:04 +0000217def get_num_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000218 exclude_atomic_group_hosts=False, valid_only=True,
219 **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000220 """
221 Same parameters as get_hosts().
222
223 @returns The number of matching hosts.
224 """
showard43a3d262008-11-12 18:17:05 +0000225 hosts = rpc_utils.get_host_query(multiple_labels,
226 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000227 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000228 valid_only, filter_data)
showard43a3d262008-11-12 18:17:05 +0000229 return hosts.count()
showard1385b162008-03-13 15:59:40 +0000230
mblighe8819cd2008-02-15 16:48:40 +0000231
232# tests
233
showard909c7a62008-07-15 21:52:38 +0000234def add_test(name, test_type, path, author=None, dependencies=None,
showard3d9899a2008-07-31 02:11:58 +0000235 experimental=True, run_verify=None, test_class=None,
showard909c7a62008-07-15 21:52:38 +0000236 test_time=None, test_category=None, description=None,
237 sync_count=1):
jadmanski0afbb632008-06-06 21:10:57 +0000238 return models.Test.add_object(name=name, test_type=test_type, path=path,
showard909c7a62008-07-15 21:52:38 +0000239 author=author, dependencies=dependencies,
240 experimental=experimental,
241 run_verify=run_verify, test_time=test_time,
242 test_category=test_category,
243 sync_count=sync_count,
jadmanski0afbb632008-06-06 21:10:57 +0000244 test_class=test_class,
245 description=description).id
mblighe8819cd2008-02-15 16:48:40 +0000246
247
248def modify_test(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000249 models.Test.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000250
251
252def delete_test(id):
jadmanski0afbb632008-06-06 21:10:57 +0000253 models.Test.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000254
255
256def get_tests(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000257 return rpc_utils.prepare_for_serialization(
258 models.Test.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000259
260
showard2b9a88b2008-06-13 20:55:03 +0000261# profilers
262
263def add_profiler(name, description=None):
264 return models.Profiler.add_object(name=name, description=description).id
265
266
267def modify_profiler(id, **data):
268 models.Profiler.smart_get(id).update_object(data)
269
270
271def delete_profiler(id):
272 models.Profiler.smart_get(id).delete()
273
274
275def get_profilers(**filter_data):
276 return rpc_utils.prepare_for_serialization(
277 models.Profiler.list_objects(filter_data))
278
279
mblighe8819cd2008-02-15 16:48:40 +0000280# users
281
282def add_user(login, access_level=None):
jadmanski0afbb632008-06-06 21:10:57 +0000283 return models.User.add_object(login=login, access_level=access_level).id
mblighe8819cd2008-02-15 16:48:40 +0000284
285
286def modify_user(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000287 models.User.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000288
289
290def delete_user(id):
jadmanski0afbb632008-06-06 21:10:57 +0000291 models.User.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000292
293
294def get_users(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000295 return rpc_utils.prepare_for_serialization(
296 models.User.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000297
298
299# acl groups
300
301def add_acl_group(name, description=None):
showard04f2cd82008-07-25 20:53:31 +0000302 group = models.AclGroup.add_object(name=name, description=description)
showard64a95952010-01-13 21:27:16 +0000303 group.users.add(models.User.current_user())
showard04f2cd82008-07-25 20:53:31 +0000304 return group.id
mblighe8819cd2008-02-15 16:48:40 +0000305
306
307def modify_acl_group(id, **data):
showard04f2cd82008-07-25 20:53:31 +0000308 group = models.AclGroup.smart_get(id)
309 group.check_for_acl_violation_acl_group()
310 group.update_object(data)
311 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000312
313
314def acl_group_add_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000315 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000316 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000317 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000318 group.users.add(*users)
mblighe8819cd2008-02-15 16:48:40 +0000319
320
321def acl_group_remove_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000322 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000323 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000324 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000325 group.users.remove(*users)
showard04f2cd82008-07-25 20:53:31 +0000326 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000327
328
329def acl_group_add_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000330 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000331 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000332 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000333 group.hosts.add(*hosts)
showard08f981b2008-06-24 21:59:03 +0000334 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000335
336
337def acl_group_remove_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000338 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000339 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000340 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000341 group.hosts.remove(*hosts)
showard08f981b2008-06-24 21:59:03 +0000342 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000343
344
345def delete_acl_group(id):
jadmanski0afbb632008-06-06 21:10:57 +0000346 models.AclGroup.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000347
348
349def get_acl_groups(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000350 acl_groups = models.AclGroup.list_objects(filter_data)
351 for acl_group in acl_groups:
352 acl_group_obj = models.AclGroup.objects.get(id=acl_group['id'])
353 acl_group['users'] = [user.login
354 for user in acl_group_obj.users.all()]
355 acl_group['hosts'] = [host.hostname
356 for host in acl_group_obj.hosts.all()]
357 return rpc_utils.prepare_for_serialization(acl_groups)
mblighe8819cd2008-02-15 16:48:40 +0000358
359
360# jobs
361
mbligh120351e2009-01-24 01:40:45 +0000362def generate_control_file(tests=(), kernel=None, label=None, profilers=(),
showard91f85102009-10-12 20:34:52 +0000363 client_control_file='', use_container=False,
showard232b7ae2009-11-10 00:46:48 +0000364 profile_only=None, upload_kernel_config=False):
jadmanski0afbb632008-06-06 21:10:57 +0000365 """
mbligh120351e2009-01-24 01:40:45 +0000366 Generates a client-side control file to load a kernel and run tests.
367
368 @param tests List of tests to run.
mbligha3c58d22009-08-24 22:01:51 +0000369 @param kernel A list of kernel info dictionaries configuring which kernels
370 to boot for this job and other options for them
mbligh120351e2009-01-24 01:40:45 +0000371 @param label Name of label to grab kernel config from.
372 @param profilers List of profilers to activate during the job.
373 @param client_control_file The contents of a client-side control file to
374 run at the end of all tests. If this is supplied, all tests must be
375 client side.
376 TODO: in the future we should support server control files directly
377 to wrap with a kernel. That'll require changing the parameter
378 name and adding a boolean to indicate if it is a client or server
379 control file.
380 @param use_container unused argument today. TODO: Enable containers
381 on the host during a client side test.
showard91f85102009-10-12 20:34:52 +0000382 @param profile_only A boolean that indicates what default profile_only
383 mode to use in the control file. Passing None will generate a
384 control file that does not explcitly set the default mode at all.
showard232b7ae2009-11-10 00:46:48 +0000385 @param upload_kernel_config: if enabled it will generate server control
386 file code that uploads the kernel config file to the client and
387 tells the client of the new (local) path when compiling the kernel;
388 the tests must be server side tests
mbligh120351e2009-01-24 01:40:45 +0000389
390 @returns a dict with the following keys:
391 control_file: str, The control file text.
392 is_server: bool, is the control file a server-side control file?
393 synch_count: How many machines the job uses per autoserv execution.
394 synch_count == 1 means the job is asynchronous.
395 dependencies: A list of the names of labels on which the job depends.
396 """
showardd86debe2009-06-10 17:37:56 +0000397 if not tests and not client_control_file:
showard2bab8f42008-11-12 18:15:22 +0000398 return dict(control_file='', is_server=False, synch_count=1,
showard989f25d2008-10-01 11:38:11 +0000399 dependencies=[])
mblighe8819cd2008-02-15 16:48:40 +0000400
showard989f25d2008-10-01 11:38:11 +0000401 cf_info, test_objects, profiler_objects, label = (
showard2b9a88b2008-06-13 20:55:03 +0000402 rpc_utils.prepare_generate_control_file(tests, kernel, label,
403 profilers))
showard989f25d2008-10-01 11:38:11 +0000404 cf_info['control_file'] = control_file.generate_control(
mbligha3c58d22009-08-24 22:01:51 +0000405 tests=test_objects, kernels=kernel, platform=label,
mbligh120351e2009-01-24 01:40:45 +0000406 profilers=profiler_objects, is_server=cf_info['is_server'],
showard232b7ae2009-11-10 00:46:48 +0000407 client_control_file=client_control_file, profile_only=profile_only,
408 upload_kernel_config=upload_kernel_config)
showard989f25d2008-10-01 11:38:11 +0000409 return cf_info
mblighe8819cd2008-02-15 16:48:40 +0000410
411
jamesren4a41e012010-07-16 22:33:48 +0000412def create_parameterized_job(name, priority, test, parameters, kernel=None,
413 label=None, profilers=(), profiler_parameters=None,
414 use_container=False, profile_only=None,
415 upload_kernel_config=False, hosts=(),
416 meta_hosts=(), one_time_hosts=(),
417 atomic_group_name=None, synch_count=None,
418 is_template=False, timeout=None,
Simran Basi34217022012-11-06 13:43:15 -0800419 max_runtime_mins=None, run_verify=True,
jamesren4a41e012010-07-16 22:33:48 +0000420 email_list='', dependencies=(), reboot_before=None,
421 reboot_after=None, parse_failed_repair=None,
422 hostless=False, keyvals=None, drone_set=None):
423 """
424 Creates and enqueues a parameterized job.
425
426 Most parameters a combination of the parameters for generate_control_file()
427 and create_job(), with the exception of:
428
429 @param test name or ID of the test to run
430 @param parameters a map of parameter name ->
431 tuple of (param value, param type)
432 @param profiler_parameters a dictionary of parameters for the profilers:
433 key: profiler name
434 value: dict of param name -> tuple of
435 (param value,
436 param type)
437 """
438 # Save the values of the passed arguments here. What we're going to do with
439 # them is pass them all to rpc_utils.get_create_job_common_args(), which
440 # will extract the subset of these arguments that apply for
441 # rpc_utils.create_job_common(), which we then pass in to that function.
442 args = locals()
443
444 # Set up the parameterized job configs
445 test_obj = models.Test.smart_get(test)
446 if test_obj.test_type == model_attributes.TestTypes.SERVER:
447 control_type = models.Job.ControlType.SERVER
448 else:
449 control_type = models.Job.ControlType.CLIENT
450
451 try:
452 label = models.Label.smart_get(label)
453 except models.Label.DoesNotExist:
454 label = None
455
456 kernel_objs = models.Kernel.create_kernels(kernel)
457 profiler_objs = [models.Profiler.smart_get(profiler)
458 for profiler in profilers]
459
460 parameterized_job = models.ParameterizedJob.objects.create(
461 test=test_obj, label=label, use_container=use_container,
462 profile_only=profile_only,
463 upload_kernel_config=upload_kernel_config)
464 parameterized_job.kernels.add(*kernel_objs)
465
466 for profiler in profiler_objs:
467 parameterized_profiler = models.ParameterizedJobProfiler.objects.create(
468 parameterized_job=parameterized_job,
469 profiler=profiler)
470 profiler_params = profiler_parameters.get(profiler.name, {})
471 for name, (value, param_type) in profiler_params.iteritems():
472 models.ParameterizedJobProfilerParameter.objects.create(
473 parameterized_job_profiler=parameterized_profiler,
474 parameter_name=name,
475 parameter_value=value,
476 parameter_type=param_type)
477
478 try:
479 for parameter in test_obj.testparameter_set.all():
480 if parameter.name in parameters:
481 param_value, param_type = parameters.pop(parameter.name)
482 parameterized_job.parameterizedjobparameter_set.create(
483 test_parameter=parameter, parameter_value=param_value,
484 parameter_type=param_type)
485
486 if parameters:
487 raise Exception('Extra parameters remain: %r' % parameters)
488
489 return rpc_utils.create_job_common(
490 parameterized_job=parameterized_job.id,
491 control_type=control_type,
492 **rpc_utils.get_create_job_common_args(args))
493 except:
494 parameterized_job.delete()
495 raise
496
497
showard12f3e322009-05-13 21:27:42 +0000498def create_job(name, priority, control_file, control_type,
499 hosts=(), meta_hosts=(), one_time_hosts=(),
500 atomic_group_name=None, synch_count=None, is_template=False,
Simran Basi34217022012-11-06 13:43:15 -0800501 timeout=None, max_runtime_mins=None, run_verify=True,
showard12f3e322009-05-13 21:27:42 +0000502 email_list='', dependencies=(), reboot_before=None,
showardc1a98d12010-01-15 00:22:22 +0000503 reboot_after=None, parse_failed_repair=None, hostless=False,
Aviv Keshetcd1ff9b2013-03-01 14:55:19 -0800504 keyvals=None, drone_set=None, image=None, parent_job_id=None,
505 test_retry=0):
jadmanski0afbb632008-06-06 21:10:57 +0000506 """\
507 Create and enqueue a job.
mblighe8819cd2008-02-15 16:48:40 +0000508
showarda1e74b32009-05-12 17:32:04 +0000509 @param name name of this job
510 @param priority Low, Medium, High, Urgent
511 @param control_file String contents of the control file.
512 @param control_type Type of control file, Client or Server.
513 @param synch_count How many machines the job uses per autoserv execution.
514 synch_count == 1 means the job is asynchronous. If an atomic group is
515 given this value is treated as a minimum.
516 @param is_template If true then create a template job.
517 @param timeout Hours after this call returns until the job times out.
Simran Basi34217022012-11-06 13:43:15 -0800518 @param max_runtime_mins Minutes from job starting time until job times out
showarda1e74b32009-05-12 17:32:04 +0000519 @param run_verify Should the host be verified before running the test?
520 @param email_list String containing emails to mail when the job is done
521 @param dependencies List of label names on which this job depends
522 @param reboot_before Never, If dirty, or Always
523 @param reboot_after Never, If all tests passed, or Always
524 @param parse_failed_repair if true, results of failed repairs launched by
525 this job will be parsed as part of the job.
showarda9545c02009-12-18 22:44:26 +0000526 @param hostless if true, create a hostless job
showardc1a98d12010-01-15 00:22:22 +0000527 @param keyvals dict of keyvals to associate with the job
showarda1e74b32009-05-12 17:32:04 +0000528
529 @param hosts List of hosts to run job on.
530 @param meta_hosts List where each entry is a label name, and for each entry
531 one host will be chosen from that label to run the job on.
532 @param one_time_hosts List of hosts not in the database to run the job on.
533 @param atomic_group_name The name of an atomic group to schedule the job on.
jamesren76fcf192010-04-21 20:39:50 +0000534 @param drone_set The name of the drone set to run this test on.
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800535 @param image OS image to install before running job.
Aviv Keshet0b9cfc92013-02-05 11:36:02 -0800536 @param parent_job_id id of a job considered to be parent of created job.
Aviv Keshetcd1ff9b2013-03-01 14:55:19 -0800537 @param test_retry: Number of times to retry test if the test did not
538 complete successfully. (optional, default: 0)
showardc92da832009-04-07 18:14:34 +0000539
540 @returns The created Job id number.
jadmanski0afbb632008-06-06 21:10:57 +0000541 """
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800542
Alex Millera713e252013-03-01 10:45:44 -0800543 # Force control files to only contain ascii characters.
544 try:
545 control_file.encode('ascii')
546 except UnicodeDecodeError as e:
547 raise error.ControlFileMalformed(str(e))
548
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800549 if image is None:
550 return rpc_utils.create_job_common(
551 **rpc_utils.get_create_job_common_args(locals()))
552
553 # When image is supplied use a known parameterized test already in the
554 # database to pass the OS image path from the front end, through the
555 # scheduler, and finally to autoserv as the --image parameter.
556
557 # The test autoupdate_ParameterizedJob is in afe_autotests and used to
558 # instantiate a Test object and from there a ParameterizedJob.
559 known_test_obj = models.Test.smart_get('autoupdate_ParameterizedJob')
560 known_parameterized_job = models.ParameterizedJob.objects.create(
561 test=known_test_obj)
562
563 # autoupdate_ParameterizedJob has a single parameter, the image parameter,
564 # stored in the table afe_test_parameters. We retrieve and set this
565 # instance of the parameter to the OS image path.
Eric Lid23bc192011-02-09 14:38:57 -0800566 image_parameter = known_test_obj.testparameter_set.get(test=known_test_obj,
567 name='image')
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800568 known_parameterized_job.parameterizedjobparameter_set.create(
569 test_parameter=image_parameter, parameter_value=image,
570 parameter_type='string')
571
572 # By passing a parameterized_job to create_job_common the job entry in
573 # the afe_jobs table will have the field parameterized_job_id set.
574 # The scheduler uses this id in the afe_parameterized_jobs table to
575 # match this job to our known test, and then with the
576 # afe_parameterized_job_parameters table to get the actual image path.
jamesren4a41e012010-07-16 22:33:48 +0000577 return rpc_utils.create_job_common(
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800578 parameterized_job=known_parameterized_job.id,
jamesren4a41e012010-07-16 22:33:48 +0000579 **rpc_utils.get_create_job_common_args(locals()))
mblighe8819cd2008-02-15 16:48:40 +0000580
581
showard9dbdcda2008-10-14 17:34:36 +0000582def abort_host_queue_entries(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000583 """\
showard9dbdcda2008-10-14 17:34:36 +0000584 Abort a set of host queue entries.
jadmanski0afbb632008-06-06 21:10:57 +0000585 """
showard9dbdcda2008-10-14 17:34:36 +0000586 query = models.HostQueueEntry.query_objects(filter_data)
showard0c185192009-01-16 03:07:57 +0000587 query = query.filter(complete=False)
showarddc817512008-11-12 18:16:41 +0000588 models.AclGroup.check_abort_permissions(query)
showard9dbdcda2008-10-14 17:34:36 +0000589 host_queue_entries = list(query.select_related())
showard2bab8f42008-11-12 18:15:22 +0000590 rpc_utils.check_abort_synchronous_jobs(host_queue_entries)
mblighe8819cd2008-02-15 16:48:40 +0000591
showard9dbdcda2008-10-14 17:34:36 +0000592 for queue_entry in host_queue_entries:
showard64a95952010-01-13 21:27:16 +0000593 queue_entry.abort()
showard9d821ab2008-07-11 16:54:29 +0000594
595
Simran Basi73dae552013-02-25 14:57:46 -0800596def _call_special_tasks_on_hosts(task, hosts):
597 """\
598 Schedules a set of hosts for a special task.
599
600 @returns A list of hostnames that a special task was created for.
601 """
602 models.AclGroup.check_for_acl_violation_hosts(hosts)
603 for host in hosts:
604 models.SpecialTask.schedule_special_task(host, task)
605 return list(sorted(host.hostname for host in hosts))
606
607
showard1ff7b2e2009-05-15 23:17:18 +0000608def reverify_hosts(**filter_data):
609 """\
610 Schedules a set of hosts for verify.
mbligh4e545a52009-12-19 05:30:39 +0000611
612 @returns A list of hostnames that a verify task was created for.
showard1ff7b2e2009-05-15 23:17:18 +0000613 """
Simran Basi73dae552013-02-25 14:57:46 -0800614 return _call_special_tasks_on_hosts(models.SpecialTask.Task.VERIFY,
615 models.Host.query_objects(filter_data))
616
617
618def repair_hosts(**filter_data):
619 """\
620 Schedules a set of hosts for repair.
621
622 @returns A list of hostnames that a repair task was created for.
623 """
624 return _call_special_tasks_on_hosts(models.SpecialTask.Task.REPAIR,
625 models.Host.query_objects(filter_data))
showard1ff7b2e2009-05-15 23:17:18 +0000626
627
mblighe8819cd2008-02-15 16:48:40 +0000628def get_jobs(not_yet_run=False, running=False, finished=False, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000629 """\
630 Extra filter args for get_jobs:
631 -not_yet_run: Include only jobs that have not yet started running.
632 -running: Include only jobs that have start running but for which not
633 all hosts have completed.
634 -finished: Include only jobs for which all hosts have completed (or
635 aborted).
636 At most one of these three fields should be specified.
637 """
638 filter_data['extra_args'] = rpc_utils.extra_job_filters(not_yet_run,
639 running,
640 finished)
showard0957a842009-05-11 19:25:08 +0000641 job_dicts = []
642 jobs = list(models.Job.query_objects(filter_data))
643 models.Job.objects.populate_relationships(jobs, models.Label,
644 'dependencies')
showardc1a98d12010-01-15 00:22:22 +0000645 models.Job.objects.populate_relationships(jobs, models.JobKeyval, 'keyvals')
showard0957a842009-05-11 19:25:08 +0000646 for job in jobs:
647 job_dict = job.get_object_dict()
648 job_dict['dependencies'] = ','.join(label.name
649 for label in job.dependencies)
showardc1a98d12010-01-15 00:22:22 +0000650 job_dict['keyvals'] = dict((keyval.key, keyval.value)
651 for keyval in job.keyvals)
Eric Lid23bc192011-02-09 14:38:57 -0800652 if job.parameterized_job:
653 job_dict['image'] = get_parameterized_autoupdate_image_url(job)
showard0957a842009-05-11 19:25:08 +0000654 job_dicts.append(job_dict)
655 return rpc_utils.prepare_for_serialization(job_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000656
657
658def get_num_jobs(not_yet_run=False, running=False, finished=False,
jadmanski0afbb632008-06-06 21:10:57 +0000659 **filter_data):
660 """\
661 See get_jobs() for documentation of extra filter parameters.
662 """
663 filter_data['extra_args'] = rpc_utils.extra_job_filters(not_yet_run,
664 running,
665 finished)
666 return models.Job.query_count(filter_data)
mblighe8819cd2008-02-15 16:48:40 +0000667
668
mblighe8819cd2008-02-15 16:48:40 +0000669def get_jobs_summary(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000670 """\
showarda8709c52008-07-03 19:44:54 +0000671 Like get_jobs(), but adds a 'status_counts' field, which is a dictionary
jadmanski0afbb632008-06-06 21:10:57 +0000672 mapping status strings to the number of hosts currently with that
673 status, i.e. {'Queued' : 4, 'Running' : 2}.
674 """
675 jobs = get_jobs(**filter_data)
676 ids = [job['id'] for job in jobs]
677 all_status_counts = models.Job.objects.get_status_counts(ids)
678 for job in jobs:
679 job['status_counts'] = all_status_counts[job['id']]
680 return rpc_utils.prepare_for_serialization(jobs)
mblighe8819cd2008-02-15 16:48:40 +0000681
682
showarda965cef2009-05-15 23:17:41 +0000683def get_info_for_clone(id, preserve_metahosts, queue_entry_filter_data=None):
showarda8709c52008-07-03 19:44:54 +0000684 """\
685 Retrieves all the information needed to clone a job.
686 """
showarda8709c52008-07-03 19:44:54 +0000687 job = models.Job.objects.get(id=id)
showard29f7cd22009-04-29 21:16:24 +0000688 job_info = rpc_utils.get_job_info(job,
showarda965cef2009-05-15 23:17:41 +0000689 preserve_metahosts,
690 queue_entry_filter_data)
showard945072f2008-09-03 20:34:59 +0000691
showardd9992fe2008-07-31 02:15:03 +0000692 host_dicts = []
showard29f7cd22009-04-29 21:16:24 +0000693 for host in job_info['hosts']:
694 host_dict = get_hosts(id=host.id)[0]
695 other_labels = host_dict['labels']
696 if host_dict['platform']:
697 other_labels.remove(host_dict['platform'])
698 host_dict['other_labels'] = ', '.join(other_labels)
showardd9992fe2008-07-31 02:15:03 +0000699 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +0000700
showard29f7cd22009-04-29 21:16:24 +0000701 for host in job_info['one_time_hosts']:
702 host_dict = dict(hostname=host.hostname,
703 id=host.id,
704 platform='(one-time host)',
705 locked_text='')
706 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +0000707
showard4d077562009-05-08 18:24:36 +0000708 # convert keys from Label objects to strings (names of labels)
showard29f7cd22009-04-29 21:16:24 +0000709 meta_host_counts = dict((meta_host.name, count) for meta_host, count
showard4d077562009-05-08 18:24:36 +0000710 in job_info['meta_host_counts'].iteritems())
showard29f7cd22009-04-29 21:16:24 +0000711
712 info = dict(job=job.get_object_dict(),
713 meta_host_counts=meta_host_counts,
714 hosts=host_dicts)
715 info['job']['dependencies'] = job_info['dependencies']
716 if job_info['atomic_group']:
717 info['atomic_group_name'] = (job_info['atomic_group']).name
718 else:
719 info['atomic_group_name'] = None
jamesren2275ef12010-04-12 18:25:06 +0000720 info['hostless'] = job_info['hostless']
jamesren76fcf192010-04-21 20:39:50 +0000721 info['drone_set'] = job.drone_set and job.drone_set.name
showarda8709c52008-07-03 19:44:54 +0000722
Eric Lid23bc192011-02-09 14:38:57 -0800723 if job.parameterized_job:
724 info['job']['image'] = get_parameterized_autoupdate_image_url(job)
725
showarda8709c52008-07-03 19:44:54 +0000726 return rpc_utils.prepare_for_serialization(info)
727
728
showard34dc5fa2008-04-24 20:58:40 +0000729# host queue entries
730
731def get_host_queue_entries(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000732 """\
showardc92da832009-04-07 18:14:34 +0000733 @returns A sequence of nested dictionaries of host and job information.
jadmanski0afbb632008-06-06 21:10:57 +0000734 """
showardc92da832009-04-07 18:14:34 +0000735 return rpc_utils.prepare_rows_as_nested_dicts(
736 models.HostQueueEntry.query_objects(filter_data),
737 ('host', 'atomic_group', 'job'))
showard34dc5fa2008-04-24 20:58:40 +0000738
739
740def get_num_host_queue_entries(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000741 """\
742 Get the number of host queue entries associated with this job.
743 """
744 return models.HostQueueEntry.query_count(filter_data)
showard34dc5fa2008-04-24 20:58:40 +0000745
746
showard1e935f12008-07-11 00:11:36 +0000747def get_hqe_percentage_complete(**filter_data):
748 """
showardc92da832009-04-07 18:14:34 +0000749 Computes the fraction of host queue entries matching the given filter data
showard1e935f12008-07-11 00:11:36 +0000750 that are complete.
751 """
752 query = models.HostQueueEntry.query_objects(filter_data)
753 complete_count = query.filter(complete=True).count()
754 total_count = query.count()
755 if total_count == 0:
756 return 1
757 return float(complete_count) / total_count
758
759
showard1a5a4082009-07-28 20:01:37 +0000760# special tasks
761
762def get_special_tasks(**filter_data):
763 return rpc_utils.prepare_rows_as_nested_dicts(
764 models.SpecialTask.query_objects(filter_data),
765 ('host', 'queue_entry'))
766
767
showardc0ac3a72009-07-08 21:14:45 +0000768# support for host detail view
769
770def get_host_queue_entries_and_special_tasks(hostname, query_start=None,
771 query_limit=None):
772 """
773 @returns an interleaved list of HostQueueEntries and SpecialTasks,
774 in approximate run order. each dict contains keys for type, host,
775 job, status, started_on, execution_path, and ID.
776 """
777 total_limit = None
778 if query_limit is not None:
779 total_limit = query_start + query_limit
780 filter_data = {'host__hostname': hostname,
781 'query_limit': total_limit,
782 'sort_by': ['-id']}
783
784 queue_entries = list(models.HostQueueEntry.query_objects(filter_data))
785 special_tasks = list(models.SpecialTask.query_objects(filter_data))
786
787 interleaved_entries = rpc_utils.interleave_entries(queue_entries,
788 special_tasks)
789 if query_start is not None:
790 interleaved_entries = interleaved_entries[query_start:]
791 if query_limit is not None:
792 interleaved_entries = interleaved_entries[:query_limit]
793 return rpc_utils.prepare_for_serialization(interleaved_entries)
794
795
796def get_num_host_queue_entries_and_special_tasks(hostname):
797 filter_data = {'host__hostname': hostname}
798 return (models.HostQueueEntry.query_count(filter_data)
799 + models.SpecialTask.query_count(filter_data))
800
801
showard29f7cd22009-04-29 21:16:24 +0000802# recurring run
803
804def get_recurring(**filter_data):
805 return rpc_utils.prepare_rows_as_nested_dicts(
806 models.RecurringRun.query_objects(filter_data),
807 ('job', 'owner'))
808
809
810def get_num_recurring(**filter_data):
811 return models.RecurringRun.query_count(filter_data)
812
813
814def delete_recurring_runs(**filter_data):
815 to_delete = models.RecurringRun.query_objects(filter_data)
816 to_delete.delete()
817
818
819def create_recurring_run(job_id, start_date, loop_period, loop_count):
showard64a95952010-01-13 21:27:16 +0000820 owner = models.User.current_user().login
showard29f7cd22009-04-29 21:16:24 +0000821 job = models.Job.objects.get(id=job_id)
822 return job.create_recurring_job(start_date=start_date,
823 loop_period=loop_period,
824 loop_count=loop_count,
825 owner=owner)
826
827
mblighe8819cd2008-02-15 16:48:40 +0000828# other
829
showarde0b63622008-08-04 20:58:47 +0000830def echo(data=""):
831 """\
832 Returns a passed in string. For doing a basic test to see if RPC calls
833 can successfully be made.
834 """
835 return data
836
837
showardb7a52fd2009-04-27 20:10:56 +0000838def get_motd():
839 """\
840 Returns the message of the day as a string.
841 """
842 return rpc_utils.get_motd()
843
844
mblighe8819cd2008-02-15 16:48:40 +0000845def get_static_data():
jadmanski0afbb632008-06-06 21:10:57 +0000846 """\
847 Returns a dictionary containing a bunch of data that shouldn't change
848 often and is otherwise inaccessible. This includes:
showardc92da832009-04-07 18:14:34 +0000849
850 priorities: List of job priority choices.
851 default_priority: Default priority value for new jobs.
852 users: Sorted list of all users.
853 labels: Sorted list of all labels.
854 atomic_groups: Sorted list of all atomic groups.
855 tests: Sorted list of all tests.
856 profilers: Sorted list of all profilers.
857 current_user: Logged-in username.
858 host_statuses: Sorted list of possible Host statuses.
859 job_statuses: Sorted list of possible HostQueueEntry statuses.
860 job_timeout_default: The default job timeout length in hours.
showarda1e74b32009-05-12 17:32:04 +0000861 parse_failed_repair_default: Default value for the parse_failed_repair job
862 option.
showardc92da832009-04-07 18:14:34 +0000863 reboot_before_options: A list of valid RebootBefore string enums.
864 reboot_after_options: A list of valid RebootAfter string enums.
865 motd: Server's message of the day.
866 status_dictionary: A mapping from one word job status names to a more
867 informative description.
jadmanski0afbb632008-06-06 21:10:57 +0000868 """
showard21baa452008-10-21 00:08:39 +0000869
870 job_fields = models.Job.get_field_dict()
jamesren76fcf192010-04-21 20:39:50 +0000871 default_drone_set_name = models.DroneSet.default_drone_set_name()
872 drone_sets = ([default_drone_set_name] +
873 sorted(drone_set.name for drone_set in
874 models.DroneSet.objects.exclude(
875 name=default_drone_set_name)))
showard21baa452008-10-21 00:08:39 +0000876
jadmanski0afbb632008-06-06 21:10:57 +0000877 result = {}
878 result['priorities'] = models.Job.Priority.choices()
showard21baa452008-10-21 00:08:39 +0000879 default_priority = job_fields['priority'].default
jadmanski0afbb632008-06-06 21:10:57 +0000880 default_string = models.Job.Priority.get_string(default_priority)
881 result['default_priority'] = default_string
882 result['users'] = get_users(sort_by=['login'])
883 result['labels'] = get_labels(sort_by=['-platform', 'name'])
showardc92da832009-04-07 18:14:34 +0000884 result['atomic_groups'] = get_atomic_groups(sort_by=['name'])
jadmanski0afbb632008-06-06 21:10:57 +0000885 result['tests'] = get_tests(sort_by=['name'])
showard2b9a88b2008-06-13 20:55:03 +0000886 result['profilers'] = get_profilers(sort_by=['name'])
showard0fc38302008-10-23 00:44:07 +0000887 result['current_user'] = rpc_utils.prepare_for_serialization(
showard64a95952010-01-13 21:27:16 +0000888 models.User.current_user().get_object_dict())
showard2b9a88b2008-06-13 20:55:03 +0000889 result['host_statuses'] = sorted(models.Host.Status.names)
mbligh5a198b92008-12-11 19:33:29 +0000890 result['job_statuses'] = sorted(models.HostQueueEntry.Status.names)
showardb1e51872008-10-07 11:08:18 +0000891 result['job_timeout_default'] = models.Job.DEFAULT_TIMEOUT
Simran Basi34217022012-11-06 13:43:15 -0800892 result['job_max_runtime_mins_default'] = (
893 models.Job.DEFAULT_MAX_RUNTIME_MINS)
showarda1e74b32009-05-12 17:32:04 +0000894 result['parse_failed_repair_default'] = bool(
895 models.Job.DEFAULT_PARSE_FAILED_REPAIR)
jamesrendd855242010-03-02 22:23:44 +0000896 result['reboot_before_options'] = model_attributes.RebootBefore.names
897 result['reboot_after_options'] = model_attributes.RebootAfter.names
showard8fbae652009-01-20 23:23:10 +0000898 result['motd'] = rpc_utils.get_motd()
jamesren76fcf192010-04-21 20:39:50 +0000899 result['drone_sets_enabled'] = models.DroneSet.drone_sets_enabled()
900 result['drone_sets'] = drone_sets
jamesren4a41e012010-07-16 22:33:48 +0000901 result['parameterized_jobs'] = models.Job.parameterized_jobs_enabled()
showard8ac29b42008-07-17 17:01:55 +0000902
showardd3dc1992009-04-22 21:01:40 +0000903 result['status_dictionary'] = {"Aborted": "Aborted",
showard8ac29b42008-07-17 17:01:55 +0000904 "Verifying": "Verifying Host",
905 "Pending": "Waiting on other hosts",
906 "Running": "Running autoserv",
907 "Completed": "Autoserv completed",
908 "Failed": "Failed to complete",
showardd823b362008-07-24 16:35:46 +0000909 "Queued": "Queued",
showard5deb6772008-11-04 21:54:33 +0000910 "Starting": "Next in host's queue",
911 "Stopped": "Other host(s) failed verify",
showardd3dc1992009-04-22 21:01:40 +0000912 "Parsing": "Awaiting parse of final results",
showard29f7cd22009-04-29 21:16:24 +0000913 "Gathering": "Gathering log files",
showard8cc058f2009-09-08 16:26:33 +0000914 "Template": "Template job for recurring run",
mbligh4608b002010-01-05 18:22:35 +0000915 "Waiting": "Waiting for scheduler action",
916 "Archiving": "Archiving results"}
jadmanski0afbb632008-06-06 21:10:57 +0000917 return result
showard29f7cd22009-04-29 21:16:24 +0000918
919
920def get_server_time():
921 return datetime.datetime.now().strftime("%Y-%m-%d %H:%M")