blob: b24e12e4a586c032f215da95bf69b89887f36b66 [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
jamesrendd855242010-03-02 22:23:44 +000036from autotest_lib.frontend.afe import models, model_logic, model_attributes
showard6d7b2ff2009-06-10 00:16:47 +000037from autotest_lib.frontend.afe import control_file, rpc_utils
showard3bb499f2008-07-03 19:42:20 +000038
mblighe8819cd2008-02-15 16:48:40 +000039
Eric Lid23bc192011-02-09 14:38:57 -080040def get_parameterized_autoupdate_image_url(job):
41 """Get the parameterized autoupdate image url from a parameterized job."""
42 known_test_obj = models.Test.smart_get('autoupdate_ParameterizedJob')
43 image_parameter = known_test_obj.testparameter_set.get(test=known_test_obj,
44 name='image')
45 para_set = job.parameterized_job.parameterizedjobparameter_set
46 job_test_para = para_set.get(test_parameter=image_parameter)
47 return job_test_para.parameter_value
48
49
mblighe8819cd2008-02-15 16:48:40 +000050# labels
51
showard989f25d2008-10-01 11:38:11 +000052def add_label(name, kernel_config=None, platform=None, only_if_needed=None):
showardc92da832009-04-07 18:14:34 +000053 return models.Label.add_object(
54 name=name, kernel_config=kernel_config, platform=platform,
55 only_if_needed=only_if_needed).id
mblighe8819cd2008-02-15 16:48:40 +000056
57
58def modify_label(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +000059 models.Label.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +000060
61
62def delete_label(id):
jadmanski0afbb632008-06-06 21:10:57 +000063 models.Label.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +000064
65
showardbbabf502008-06-06 00:02:02 +000066def label_add_hosts(id, hosts):
showardbe3ec042008-11-12 18:16:07 +000067 host_objs = models.Host.smart_get_bulk(hosts)
showardcafd16e2009-05-29 18:37:49 +000068 label = models.Label.smart_get(id)
69 if label.platform:
70 models.Host.check_no_platform(host_objs)
71 label.host_set.add(*host_objs)
showardbbabf502008-06-06 00:02:02 +000072
73
74def label_remove_hosts(id, hosts):
showardbe3ec042008-11-12 18:16:07 +000075 host_objs = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +000076 models.Label.smart_get(id).host_set.remove(*host_objs)
showardbbabf502008-06-06 00:02:02 +000077
78
mblighe8819cd2008-02-15 16:48:40 +000079def get_labels(**filter_data):
showardc92da832009-04-07 18:14:34 +000080 """\
81 @returns A sequence of nested dictionaries of label information.
82 """
83 return rpc_utils.prepare_rows_as_nested_dicts(
84 models.Label.query_objects(filter_data),
85 ('atomic_group',))
86
87
88# atomic groups
89
showarde9450c92009-06-30 01:58:52 +000090def add_atomic_group(name, max_number_of_machines=None, description=None):
showardc92da832009-04-07 18:14:34 +000091 return models.AtomicGroup.add_object(
92 name=name, max_number_of_machines=max_number_of_machines,
93 description=description).id
94
95
96def modify_atomic_group(id, **data):
97 models.AtomicGroup.smart_get(id).update_object(data)
98
99
100def delete_atomic_group(id):
101 models.AtomicGroup.smart_get(id).delete()
102
103
104def atomic_group_add_labels(id, labels):
105 label_objs = models.Label.smart_get_bulk(labels)
106 models.AtomicGroup.smart_get(id).label_set.add(*label_objs)
107
108
109def atomic_group_remove_labels(id, labels):
110 label_objs = models.Label.smart_get_bulk(labels)
111 models.AtomicGroup.smart_get(id).label_set.remove(*label_objs)
112
113
114def get_atomic_groups(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000115 return rpc_utils.prepare_for_serialization(
showardc92da832009-04-07 18:14:34 +0000116 models.AtomicGroup.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000117
118
119# hosts
120
showarddf062562008-07-03 19:56:37 +0000121def add_host(hostname, status=None, locked=None, protection=None):
jadmanski0afbb632008-06-06 21:10:57 +0000122 return models.Host.add_object(hostname=hostname, status=status,
showarddf062562008-07-03 19:56:37 +0000123 locked=locked, protection=protection).id
mblighe8819cd2008-02-15 16:48:40 +0000124
125
126def modify_host(id, **data):
showardbe0d8692009-08-20 23:42:44 +0000127 rpc_utils.check_modify_host(data)
showardce7c0922009-09-11 18:39:24 +0000128 host = models.Host.smart_get(id)
129 rpc_utils.check_modify_host_locking(host, data)
130 host.update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000131
132
showard276f9442009-05-20 00:33:16 +0000133def modify_hosts(host_filter_data, update_data):
134 """
showardbe0d8692009-08-20 23:42:44 +0000135 @param host_filter_data: Filters out which hosts to modify.
136 @param update_data: A dictionary with the changes to make to the hosts.
showard276f9442009-05-20 00:33:16 +0000137 """
showardbe0d8692009-08-20 23:42:44 +0000138 rpc_utils.check_modify_host(update_data)
showard276f9442009-05-20 00:33:16 +0000139 hosts = models.Host.query_objects(host_filter_data)
140 for host in hosts:
141 host.update_object(update_data)
142
143
mblighe8819cd2008-02-15 16:48:40 +0000144def host_add_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000145 labels = models.Label.smart_get_bulk(labels)
showardcafd16e2009-05-29 18:37:49 +0000146 host = models.Host.smart_get(id)
147
148 platforms = [label.name for label in labels if label.platform]
149 if len(platforms) > 1:
150 raise model_logic.ValidationError(
151 {'labels': 'Adding more than one platform label: %s' %
152 ', '.join(platforms)})
153 if len(platforms) == 1:
154 models.Host.check_no_platform([host])
155 host.labels.add(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000156
157
158def host_remove_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000159 labels = models.Label.smart_get_bulk(labels)
jadmanski0afbb632008-06-06 21:10:57 +0000160 models.Host.smart_get(id).labels.remove(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000161
162
showard0957a842009-05-11 19:25:08 +0000163def set_host_attribute(attribute, value, **host_filter_data):
164 """
165 @param attribute string name of attribute
166 @param value string, or None to delete an attribute
167 @param host_filter_data filter data to apply to Hosts to choose hosts to act
168 upon
169 """
170 assert host_filter_data # disallow accidental actions on all hosts
171 hosts = models.Host.query_objects(host_filter_data)
172 models.AclGroup.check_for_acl_violation_hosts(hosts)
173
174 for host in hosts:
showardf8b19042009-05-12 17:22:49 +0000175 host.set_or_delete_attribute(attribute, value)
showard0957a842009-05-11 19:25:08 +0000176
177
mblighe8819cd2008-02-15 16:48:40 +0000178def delete_host(id):
jadmanski0afbb632008-06-06 21:10:57 +0000179 models.Host.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000180
181
showard87cc38f2009-08-20 23:37:04 +0000182def get_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000183 exclude_atomic_group_hosts=False, valid_only=True, **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000184 """
185 @param multiple_labels: match hosts in all of the labels given. Should
186 be a list of label names.
187 @param exclude_only_if_needed_labels: Exclude hosts with at least one
188 "only_if_needed" label applied.
189 @param exclude_atomic_group_hosts: Exclude hosts that have one or more
190 atomic group labels associated with them.
jadmanski0afbb632008-06-06 21:10:57 +0000191 """
showard43a3d262008-11-12 18:17:05 +0000192 hosts = rpc_utils.get_host_query(multiple_labels,
193 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000194 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000195 valid_only, filter_data)
showard0957a842009-05-11 19:25:08 +0000196 hosts = list(hosts)
197 models.Host.objects.populate_relationships(hosts, models.Label,
198 'label_list')
199 models.Host.objects.populate_relationships(hosts, models.AclGroup,
200 'acl_list')
201 models.Host.objects.populate_relationships(hosts, models.HostAttribute,
202 'attribute_list')
showard43a3d262008-11-12 18:17:05 +0000203 host_dicts = []
204 for host_obj in hosts:
205 host_dict = host_obj.get_object_dict()
showard0957a842009-05-11 19:25:08 +0000206 host_dict['labels'] = [label.name for label in host_obj.label_list]
showard909c9142009-07-07 20:54:42 +0000207 host_dict['platform'], host_dict['atomic_group'] = (rpc_utils.
208 find_platform_and_atomic_group(host_obj))
showard0957a842009-05-11 19:25:08 +0000209 host_dict['acls'] = [acl.name for acl in host_obj.acl_list]
210 host_dict['attributes'] = dict((attribute.attribute, attribute.value)
211 for attribute in host_obj.attribute_list)
showard43a3d262008-11-12 18:17:05 +0000212 host_dicts.append(host_dict)
213 return rpc_utils.prepare_for_serialization(host_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000214
215
showard87cc38f2009-08-20 23:37:04 +0000216def get_num_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000217 exclude_atomic_group_hosts=False, valid_only=True,
218 **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000219 """
220 Same parameters as get_hosts().
221
222 @returns The number of matching hosts.
223 """
showard43a3d262008-11-12 18:17:05 +0000224 hosts = rpc_utils.get_host_query(multiple_labels,
225 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000226 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000227 valid_only, filter_data)
showard43a3d262008-11-12 18:17:05 +0000228 return hosts.count()
showard1385b162008-03-13 15:59:40 +0000229
mblighe8819cd2008-02-15 16:48:40 +0000230
231# tests
232
showard909c7a62008-07-15 21:52:38 +0000233def add_test(name, test_type, path, author=None, dependencies=None,
showard3d9899a2008-07-31 02:11:58 +0000234 experimental=True, run_verify=None, test_class=None,
showard909c7a62008-07-15 21:52:38 +0000235 test_time=None, test_category=None, description=None,
236 sync_count=1):
jadmanski0afbb632008-06-06 21:10:57 +0000237 return models.Test.add_object(name=name, test_type=test_type, path=path,
showard909c7a62008-07-15 21:52:38 +0000238 author=author, dependencies=dependencies,
239 experimental=experimental,
240 run_verify=run_verify, test_time=test_time,
241 test_category=test_category,
242 sync_count=sync_count,
jadmanski0afbb632008-06-06 21:10:57 +0000243 test_class=test_class,
244 description=description).id
mblighe8819cd2008-02-15 16:48:40 +0000245
246
247def modify_test(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000248 models.Test.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000249
250
251def delete_test(id):
jadmanski0afbb632008-06-06 21:10:57 +0000252 models.Test.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000253
254
255def get_tests(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000256 return rpc_utils.prepare_for_serialization(
257 models.Test.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000258
259
showard2b9a88b2008-06-13 20:55:03 +0000260# profilers
261
262def add_profiler(name, description=None):
263 return models.Profiler.add_object(name=name, description=description).id
264
265
266def modify_profiler(id, **data):
267 models.Profiler.smart_get(id).update_object(data)
268
269
270def delete_profiler(id):
271 models.Profiler.smart_get(id).delete()
272
273
274def get_profilers(**filter_data):
275 return rpc_utils.prepare_for_serialization(
276 models.Profiler.list_objects(filter_data))
277
278
mblighe8819cd2008-02-15 16:48:40 +0000279# users
280
281def add_user(login, access_level=None):
jadmanski0afbb632008-06-06 21:10:57 +0000282 return models.User.add_object(login=login, access_level=access_level).id
mblighe8819cd2008-02-15 16:48:40 +0000283
284
285def modify_user(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000286 models.User.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000287
288
289def delete_user(id):
jadmanski0afbb632008-06-06 21:10:57 +0000290 models.User.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000291
292
293def get_users(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000294 return rpc_utils.prepare_for_serialization(
295 models.User.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000296
297
298# acl groups
299
300def add_acl_group(name, description=None):
showard04f2cd82008-07-25 20:53:31 +0000301 group = models.AclGroup.add_object(name=name, description=description)
showard64a95952010-01-13 21:27:16 +0000302 group.users.add(models.User.current_user())
showard04f2cd82008-07-25 20:53:31 +0000303 return group.id
mblighe8819cd2008-02-15 16:48:40 +0000304
305
306def modify_acl_group(id, **data):
showard04f2cd82008-07-25 20:53:31 +0000307 group = models.AclGroup.smart_get(id)
308 group.check_for_acl_violation_acl_group()
309 group.update_object(data)
310 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000311
312
313def acl_group_add_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000314 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000315 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000316 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000317 group.users.add(*users)
mblighe8819cd2008-02-15 16:48:40 +0000318
319
320def acl_group_remove_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000321 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000322 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000323 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000324 group.users.remove(*users)
showard04f2cd82008-07-25 20:53:31 +0000325 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000326
327
328def acl_group_add_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000329 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000330 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000331 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000332 group.hosts.add(*hosts)
showard08f981b2008-06-24 21:59:03 +0000333 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000334
335
336def acl_group_remove_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000337 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000338 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000339 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000340 group.hosts.remove(*hosts)
showard08f981b2008-06-24 21:59:03 +0000341 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000342
343
344def delete_acl_group(id):
jadmanski0afbb632008-06-06 21:10:57 +0000345 models.AclGroup.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000346
347
348def get_acl_groups(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000349 acl_groups = models.AclGroup.list_objects(filter_data)
350 for acl_group in acl_groups:
351 acl_group_obj = models.AclGroup.objects.get(id=acl_group['id'])
352 acl_group['users'] = [user.login
353 for user in acl_group_obj.users.all()]
354 acl_group['hosts'] = [host.hostname
355 for host in acl_group_obj.hosts.all()]
356 return rpc_utils.prepare_for_serialization(acl_groups)
mblighe8819cd2008-02-15 16:48:40 +0000357
358
359# jobs
360
mbligh120351e2009-01-24 01:40:45 +0000361def generate_control_file(tests=(), kernel=None, label=None, profilers=(),
showard91f85102009-10-12 20:34:52 +0000362 client_control_file='', use_container=False,
showard232b7ae2009-11-10 00:46:48 +0000363 profile_only=None, upload_kernel_config=False):
jadmanski0afbb632008-06-06 21:10:57 +0000364 """
mbligh120351e2009-01-24 01:40:45 +0000365 Generates a client-side control file to load a kernel and run tests.
366
367 @param tests List of tests to run.
mbligha3c58d22009-08-24 22:01:51 +0000368 @param kernel A list of kernel info dictionaries configuring which kernels
369 to boot for this job and other options for them
mbligh120351e2009-01-24 01:40:45 +0000370 @param label Name of label to grab kernel config from.
371 @param profilers List of profilers to activate during the job.
372 @param client_control_file The contents of a client-side control file to
373 run at the end of all tests. If this is supplied, all tests must be
374 client side.
375 TODO: in the future we should support server control files directly
376 to wrap with a kernel. That'll require changing the parameter
377 name and adding a boolean to indicate if it is a client or server
378 control file.
379 @param use_container unused argument today. TODO: Enable containers
380 on the host during a client side test.
showard91f85102009-10-12 20:34:52 +0000381 @param profile_only A boolean that indicates what default profile_only
382 mode to use in the control file. Passing None will generate a
383 control file that does not explcitly set the default mode at all.
showard232b7ae2009-11-10 00:46:48 +0000384 @param upload_kernel_config: if enabled it will generate server control
385 file code that uploads the kernel config file to the client and
386 tells the client of the new (local) path when compiling the kernel;
387 the tests must be server side tests
mbligh120351e2009-01-24 01:40:45 +0000388
389 @returns a dict with the following keys:
390 control_file: str, The control file text.
391 is_server: bool, is the control file a server-side control file?
392 synch_count: How many machines the job uses per autoserv execution.
393 synch_count == 1 means the job is asynchronous.
394 dependencies: A list of the names of labels on which the job depends.
395 """
showardd86debe2009-06-10 17:37:56 +0000396 if not tests and not client_control_file:
showard2bab8f42008-11-12 18:15:22 +0000397 return dict(control_file='', is_server=False, synch_count=1,
showard989f25d2008-10-01 11:38:11 +0000398 dependencies=[])
mblighe8819cd2008-02-15 16:48:40 +0000399
showard989f25d2008-10-01 11:38:11 +0000400 cf_info, test_objects, profiler_objects, label = (
showard2b9a88b2008-06-13 20:55:03 +0000401 rpc_utils.prepare_generate_control_file(tests, kernel, label,
402 profilers))
showard989f25d2008-10-01 11:38:11 +0000403 cf_info['control_file'] = control_file.generate_control(
mbligha3c58d22009-08-24 22:01:51 +0000404 tests=test_objects, kernels=kernel, platform=label,
mbligh120351e2009-01-24 01:40:45 +0000405 profilers=profiler_objects, is_server=cf_info['is_server'],
showard232b7ae2009-11-10 00:46:48 +0000406 client_control_file=client_control_file, profile_only=profile_only,
407 upload_kernel_config=upload_kernel_config)
showard989f25d2008-10-01 11:38:11 +0000408 return cf_info
mblighe8819cd2008-02-15 16:48:40 +0000409
410
jamesren4a41e012010-07-16 22:33:48 +0000411def create_parameterized_job(name, priority, test, parameters, kernel=None,
412 label=None, profilers=(), profiler_parameters=None,
413 use_container=False, profile_only=None,
414 upload_kernel_config=False, hosts=(),
415 meta_hosts=(), one_time_hosts=(),
416 atomic_group_name=None, synch_count=None,
417 is_template=False, timeout=None,
Simran Basi34217022012-11-06 13:43:15 -0800418 max_runtime_mins=None, run_verify=True,
jamesren4a41e012010-07-16 22:33:48 +0000419 email_list='', dependencies=(), reboot_before=None,
420 reboot_after=None, parse_failed_repair=None,
421 hostless=False, keyvals=None, drone_set=None):
422 """
423 Creates and enqueues a parameterized job.
424
425 Most parameters a combination of the parameters for generate_control_file()
426 and create_job(), with the exception of:
427
428 @param test name or ID of the test to run
429 @param parameters a map of parameter name ->
430 tuple of (param value, param type)
431 @param profiler_parameters a dictionary of parameters for the profilers:
432 key: profiler name
433 value: dict of param name -> tuple of
434 (param value,
435 param type)
436 """
437 # Save the values of the passed arguments here. What we're going to do with
438 # them is pass them all to rpc_utils.get_create_job_common_args(), which
439 # will extract the subset of these arguments that apply for
440 # rpc_utils.create_job_common(), which we then pass in to that function.
441 args = locals()
442
443 # Set up the parameterized job configs
444 test_obj = models.Test.smart_get(test)
445 if test_obj.test_type == model_attributes.TestTypes.SERVER:
446 control_type = models.Job.ControlType.SERVER
447 else:
448 control_type = models.Job.ControlType.CLIENT
449
450 try:
451 label = models.Label.smart_get(label)
452 except models.Label.DoesNotExist:
453 label = None
454
455 kernel_objs = models.Kernel.create_kernels(kernel)
456 profiler_objs = [models.Profiler.smart_get(profiler)
457 for profiler in profilers]
458
459 parameterized_job = models.ParameterizedJob.objects.create(
460 test=test_obj, label=label, use_container=use_container,
461 profile_only=profile_only,
462 upload_kernel_config=upload_kernel_config)
463 parameterized_job.kernels.add(*kernel_objs)
464
465 for profiler in profiler_objs:
466 parameterized_profiler = models.ParameterizedJobProfiler.objects.create(
467 parameterized_job=parameterized_job,
468 profiler=profiler)
469 profiler_params = profiler_parameters.get(profiler.name, {})
470 for name, (value, param_type) in profiler_params.iteritems():
471 models.ParameterizedJobProfilerParameter.objects.create(
472 parameterized_job_profiler=parameterized_profiler,
473 parameter_name=name,
474 parameter_value=value,
475 parameter_type=param_type)
476
477 try:
478 for parameter in test_obj.testparameter_set.all():
479 if parameter.name in parameters:
480 param_value, param_type = parameters.pop(parameter.name)
481 parameterized_job.parameterizedjobparameter_set.create(
482 test_parameter=parameter, parameter_value=param_value,
483 parameter_type=param_type)
484
485 if parameters:
486 raise Exception('Extra parameters remain: %r' % parameters)
487
488 return rpc_utils.create_job_common(
489 parameterized_job=parameterized_job.id,
490 control_type=control_type,
491 **rpc_utils.get_create_job_common_args(args))
492 except:
493 parameterized_job.delete()
494 raise
495
496
showard12f3e322009-05-13 21:27:42 +0000497def create_job(name, priority, control_file, control_type,
498 hosts=(), meta_hosts=(), one_time_hosts=(),
499 atomic_group_name=None, synch_count=None, is_template=False,
Simran Basi34217022012-11-06 13:43:15 -0800500 timeout=None, max_runtime_mins=None, run_verify=True,
showard12f3e322009-05-13 21:27:42 +0000501 email_list='', dependencies=(), reboot_before=None,
showardc1a98d12010-01-15 00:22:22 +0000502 reboot_after=None, parse_failed_repair=None, hostless=False,
Aviv Keshet0b9cfc92013-02-05 11:36:02 -0800503 keyvals=None, drone_set=None, image=None, parent_job_id=None):
jadmanski0afbb632008-06-06 21:10:57 +0000504 """\
505 Create and enqueue a job.
mblighe8819cd2008-02-15 16:48:40 +0000506
showarda1e74b32009-05-12 17:32:04 +0000507 @param name name of this job
508 @param priority Low, Medium, High, Urgent
509 @param control_file String contents of the control file.
510 @param control_type Type of control file, Client or Server.
511 @param synch_count How many machines the job uses per autoserv execution.
512 synch_count == 1 means the job is asynchronous. If an atomic group is
513 given this value is treated as a minimum.
514 @param is_template If true then create a template job.
515 @param timeout Hours after this call returns until the job times out.
Simran Basi34217022012-11-06 13:43:15 -0800516 @param max_runtime_mins Minutes from job starting time until job times out
showarda1e74b32009-05-12 17:32:04 +0000517 @param run_verify Should the host be verified before running the test?
518 @param email_list String containing emails to mail when the job is done
519 @param dependencies List of label names on which this job depends
520 @param reboot_before Never, If dirty, or Always
521 @param reboot_after Never, If all tests passed, or Always
522 @param parse_failed_repair if true, results of failed repairs launched by
523 this job will be parsed as part of the job.
showarda9545c02009-12-18 22:44:26 +0000524 @param hostless if true, create a hostless job
showardc1a98d12010-01-15 00:22:22 +0000525 @param keyvals dict of keyvals to associate with the job
showarda1e74b32009-05-12 17:32:04 +0000526
527 @param hosts List of hosts to run job on.
528 @param meta_hosts List where each entry is a label name, and for each entry
529 one host will be chosen from that label to run the job on.
530 @param one_time_hosts List of hosts not in the database to run the job on.
531 @param atomic_group_name The name of an atomic group to schedule the job on.
jamesren76fcf192010-04-21 20:39:50 +0000532 @param drone_set The name of the drone set to run this test on.
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800533 @param image OS image to install before running job.
Aviv Keshet0b9cfc92013-02-05 11:36:02 -0800534 @param parent_job_id id of a job considered to be parent of created job.
showarda1e74b32009-05-12 17:32:04 +0000535
showardc92da832009-04-07 18:14:34 +0000536
537 @returns The created Job id number.
jadmanski0afbb632008-06-06 21:10:57 +0000538 """
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800539
540 if image is None:
541 return rpc_utils.create_job_common(
542 **rpc_utils.get_create_job_common_args(locals()))
543
544 # When image is supplied use a known parameterized test already in the
545 # database to pass the OS image path from the front end, through the
546 # scheduler, and finally to autoserv as the --image parameter.
547
548 # The test autoupdate_ParameterizedJob is in afe_autotests and used to
549 # instantiate a Test object and from there a ParameterizedJob.
550 known_test_obj = models.Test.smart_get('autoupdate_ParameterizedJob')
551 known_parameterized_job = models.ParameterizedJob.objects.create(
552 test=known_test_obj)
553
554 # autoupdate_ParameterizedJob has a single parameter, the image parameter,
555 # stored in the table afe_test_parameters. We retrieve and set this
556 # instance of the parameter to the OS image path.
Eric Lid23bc192011-02-09 14:38:57 -0800557 image_parameter = known_test_obj.testparameter_set.get(test=known_test_obj,
558 name='image')
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800559 known_parameterized_job.parameterizedjobparameter_set.create(
560 test_parameter=image_parameter, parameter_value=image,
561 parameter_type='string')
562
563 # By passing a parameterized_job to create_job_common the job entry in
564 # the afe_jobs table will have the field parameterized_job_id set.
565 # The scheduler uses this id in the afe_parameterized_jobs table to
566 # match this job to our known test, and then with the
567 # afe_parameterized_job_parameters table to get the actual image path.
jamesren4a41e012010-07-16 22:33:48 +0000568 return rpc_utils.create_job_common(
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800569 parameterized_job=known_parameterized_job.id,
jamesren4a41e012010-07-16 22:33:48 +0000570 **rpc_utils.get_create_job_common_args(locals()))
mblighe8819cd2008-02-15 16:48:40 +0000571
572
showard9dbdcda2008-10-14 17:34:36 +0000573def abort_host_queue_entries(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000574 """\
showard9dbdcda2008-10-14 17:34:36 +0000575 Abort a set of host queue entries.
jadmanski0afbb632008-06-06 21:10:57 +0000576 """
showard9dbdcda2008-10-14 17:34:36 +0000577 query = models.HostQueueEntry.query_objects(filter_data)
showard0c185192009-01-16 03:07:57 +0000578 query = query.filter(complete=False)
showarddc817512008-11-12 18:16:41 +0000579 models.AclGroup.check_abort_permissions(query)
showard9dbdcda2008-10-14 17:34:36 +0000580 host_queue_entries = list(query.select_related())
showard2bab8f42008-11-12 18:15:22 +0000581 rpc_utils.check_abort_synchronous_jobs(host_queue_entries)
mblighe8819cd2008-02-15 16:48:40 +0000582
showard9dbdcda2008-10-14 17:34:36 +0000583 for queue_entry in host_queue_entries:
showard64a95952010-01-13 21:27:16 +0000584 queue_entry.abort()
showard9d821ab2008-07-11 16:54:29 +0000585
586
showard1ff7b2e2009-05-15 23:17:18 +0000587def reverify_hosts(**filter_data):
588 """\
589 Schedules a set of hosts for verify.
mbligh4e545a52009-12-19 05:30:39 +0000590
591 @returns A list of hostnames that a verify task was created for.
showard1ff7b2e2009-05-15 23:17:18 +0000592 """
593 hosts = models.Host.query_objects(filter_data)
594 models.AclGroup.check_for_acl_violation_hosts(hosts)
showardc5103442010-01-15 00:20:26 +0000595 for host in hosts:
596 models.SpecialTask.schedule_special_task(host,
597 models.SpecialTask.Task.VERIFY)
mbligh4e545a52009-12-19 05:30:39 +0000598 return list(sorted(host.hostname for host in hosts))
showard1ff7b2e2009-05-15 23:17:18 +0000599
600
mblighe8819cd2008-02-15 16:48:40 +0000601def get_jobs(not_yet_run=False, running=False, finished=False, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000602 """\
603 Extra filter args for get_jobs:
604 -not_yet_run: Include only jobs that have not yet started running.
605 -running: Include only jobs that have start running but for which not
606 all hosts have completed.
607 -finished: Include only jobs for which all hosts have completed (or
608 aborted).
609 At most one of these three fields should be specified.
610 """
611 filter_data['extra_args'] = rpc_utils.extra_job_filters(not_yet_run,
612 running,
613 finished)
showard0957a842009-05-11 19:25:08 +0000614 job_dicts = []
615 jobs = list(models.Job.query_objects(filter_data))
616 models.Job.objects.populate_relationships(jobs, models.Label,
617 'dependencies')
showardc1a98d12010-01-15 00:22:22 +0000618 models.Job.objects.populate_relationships(jobs, models.JobKeyval, 'keyvals')
showard0957a842009-05-11 19:25:08 +0000619 for job in jobs:
620 job_dict = job.get_object_dict()
621 job_dict['dependencies'] = ','.join(label.name
622 for label in job.dependencies)
showardc1a98d12010-01-15 00:22:22 +0000623 job_dict['keyvals'] = dict((keyval.key, keyval.value)
624 for keyval in job.keyvals)
Eric Lid23bc192011-02-09 14:38:57 -0800625 if job.parameterized_job:
626 job_dict['image'] = get_parameterized_autoupdate_image_url(job)
showard0957a842009-05-11 19:25:08 +0000627 job_dicts.append(job_dict)
628 return rpc_utils.prepare_for_serialization(job_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000629
630
631def get_num_jobs(not_yet_run=False, running=False, finished=False,
jadmanski0afbb632008-06-06 21:10:57 +0000632 **filter_data):
633 """\
634 See get_jobs() for documentation of extra filter parameters.
635 """
636 filter_data['extra_args'] = rpc_utils.extra_job_filters(not_yet_run,
637 running,
638 finished)
639 return models.Job.query_count(filter_data)
mblighe8819cd2008-02-15 16:48:40 +0000640
641
mblighe8819cd2008-02-15 16:48:40 +0000642def get_jobs_summary(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000643 """\
showarda8709c52008-07-03 19:44:54 +0000644 Like get_jobs(), but adds a 'status_counts' field, which is a dictionary
jadmanski0afbb632008-06-06 21:10:57 +0000645 mapping status strings to the number of hosts currently with that
646 status, i.e. {'Queued' : 4, 'Running' : 2}.
647 """
648 jobs = get_jobs(**filter_data)
649 ids = [job['id'] for job in jobs]
650 all_status_counts = models.Job.objects.get_status_counts(ids)
651 for job in jobs:
652 job['status_counts'] = all_status_counts[job['id']]
653 return rpc_utils.prepare_for_serialization(jobs)
mblighe8819cd2008-02-15 16:48:40 +0000654
655
showarda965cef2009-05-15 23:17:41 +0000656def get_info_for_clone(id, preserve_metahosts, queue_entry_filter_data=None):
showarda8709c52008-07-03 19:44:54 +0000657 """\
658 Retrieves all the information needed to clone a job.
659 """
showarda8709c52008-07-03 19:44:54 +0000660 job = models.Job.objects.get(id=id)
showard29f7cd22009-04-29 21:16:24 +0000661 job_info = rpc_utils.get_job_info(job,
showarda965cef2009-05-15 23:17:41 +0000662 preserve_metahosts,
663 queue_entry_filter_data)
showard945072f2008-09-03 20:34:59 +0000664
showardd9992fe2008-07-31 02:15:03 +0000665 host_dicts = []
showard29f7cd22009-04-29 21:16:24 +0000666 for host in job_info['hosts']:
667 host_dict = get_hosts(id=host.id)[0]
668 other_labels = host_dict['labels']
669 if host_dict['platform']:
670 other_labels.remove(host_dict['platform'])
671 host_dict['other_labels'] = ', '.join(other_labels)
showardd9992fe2008-07-31 02:15:03 +0000672 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +0000673
showard29f7cd22009-04-29 21:16:24 +0000674 for host in job_info['one_time_hosts']:
675 host_dict = dict(hostname=host.hostname,
676 id=host.id,
677 platform='(one-time host)',
678 locked_text='')
679 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +0000680
showard4d077562009-05-08 18:24:36 +0000681 # convert keys from Label objects to strings (names of labels)
showard29f7cd22009-04-29 21:16:24 +0000682 meta_host_counts = dict((meta_host.name, count) for meta_host, count
showard4d077562009-05-08 18:24:36 +0000683 in job_info['meta_host_counts'].iteritems())
showard29f7cd22009-04-29 21:16:24 +0000684
685 info = dict(job=job.get_object_dict(),
686 meta_host_counts=meta_host_counts,
687 hosts=host_dicts)
688 info['job']['dependencies'] = job_info['dependencies']
689 if job_info['atomic_group']:
690 info['atomic_group_name'] = (job_info['atomic_group']).name
691 else:
692 info['atomic_group_name'] = None
jamesren2275ef12010-04-12 18:25:06 +0000693 info['hostless'] = job_info['hostless']
jamesren76fcf192010-04-21 20:39:50 +0000694 info['drone_set'] = job.drone_set and job.drone_set.name
showarda8709c52008-07-03 19:44:54 +0000695
Eric Lid23bc192011-02-09 14:38:57 -0800696 if job.parameterized_job:
697 info['job']['image'] = get_parameterized_autoupdate_image_url(job)
698
showarda8709c52008-07-03 19:44:54 +0000699 return rpc_utils.prepare_for_serialization(info)
700
701
showard34dc5fa2008-04-24 20:58:40 +0000702# host queue entries
703
704def get_host_queue_entries(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000705 """\
showardc92da832009-04-07 18:14:34 +0000706 @returns A sequence of nested dictionaries of host and job information.
jadmanski0afbb632008-06-06 21:10:57 +0000707 """
showardc92da832009-04-07 18:14:34 +0000708 return rpc_utils.prepare_rows_as_nested_dicts(
709 models.HostQueueEntry.query_objects(filter_data),
710 ('host', 'atomic_group', 'job'))
showard34dc5fa2008-04-24 20:58:40 +0000711
712
713def get_num_host_queue_entries(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000714 """\
715 Get the number of host queue entries associated with this job.
716 """
717 return models.HostQueueEntry.query_count(filter_data)
showard34dc5fa2008-04-24 20:58:40 +0000718
719
showard1e935f12008-07-11 00:11:36 +0000720def get_hqe_percentage_complete(**filter_data):
721 """
showardc92da832009-04-07 18:14:34 +0000722 Computes the fraction of host queue entries matching the given filter data
showard1e935f12008-07-11 00:11:36 +0000723 that are complete.
724 """
725 query = models.HostQueueEntry.query_objects(filter_data)
726 complete_count = query.filter(complete=True).count()
727 total_count = query.count()
728 if total_count == 0:
729 return 1
730 return float(complete_count) / total_count
731
732
showard1a5a4082009-07-28 20:01:37 +0000733# special tasks
734
735def get_special_tasks(**filter_data):
736 return rpc_utils.prepare_rows_as_nested_dicts(
737 models.SpecialTask.query_objects(filter_data),
738 ('host', 'queue_entry'))
739
740
showardc0ac3a72009-07-08 21:14:45 +0000741# support for host detail view
742
743def get_host_queue_entries_and_special_tasks(hostname, query_start=None,
744 query_limit=None):
745 """
746 @returns an interleaved list of HostQueueEntries and SpecialTasks,
747 in approximate run order. each dict contains keys for type, host,
748 job, status, started_on, execution_path, and ID.
749 """
750 total_limit = None
751 if query_limit is not None:
752 total_limit = query_start + query_limit
753 filter_data = {'host__hostname': hostname,
754 'query_limit': total_limit,
755 'sort_by': ['-id']}
756
757 queue_entries = list(models.HostQueueEntry.query_objects(filter_data))
758 special_tasks = list(models.SpecialTask.query_objects(filter_data))
759
760 interleaved_entries = rpc_utils.interleave_entries(queue_entries,
761 special_tasks)
762 if query_start is not None:
763 interleaved_entries = interleaved_entries[query_start:]
764 if query_limit is not None:
765 interleaved_entries = interleaved_entries[:query_limit]
766 return rpc_utils.prepare_for_serialization(interleaved_entries)
767
768
769def get_num_host_queue_entries_and_special_tasks(hostname):
770 filter_data = {'host__hostname': hostname}
771 return (models.HostQueueEntry.query_count(filter_data)
772 + models.SpecialTask.query_count(filter_data))
773
774
showard29f7cd22009-04-29 21:16:24 +0000775# recurring run
776
777def get_recurring(**filter_data):
778 return rpc_utils.prepare_rows_as_nested_dicts(
779 models.RecurringRun.query_objects(filter_data),
780 ('job', 'owner'))
781
782
783def get_num_recurring(**filter_data):
784 return models.RecurringRun.query_count(filter_data)
785
786
787def delete_recurring_runs(**filter_data):
788 to_delete = models.RecurringRun.query_objects(filter_data)
789 to_delete.delete()
790
791
792def create_recurring_run(job_id, start_date, loop_period, loop_count):
showard64a95952010-01-13 21:27:16 +0000793 owner = models.User.current_user().login
showard29f7cd22009-04-29 21:16:24 +0000794 job = models.Job.objects.get(id=job_id)
795 return job.create_recurring_job(start_date=start_date,
796 loop_period=loop_period,
797 loop_count=loop_count,
798 owner=owner)
799
800
mblighe8819cd2008-02-15 16:48:40 +0000801# other
802
showarde0b63622008-08-04 20:58:47 +0000803def echo(data=""):
804 """\
805 Returns a passed in string. For doing a basic test to see if RPC calls
806 can successfully be made.
807 """
808 return data
809
810
showardb7a52fd2009-04-27 20:10:56 +0000811def get_motd():
812 """\
813 Returns the message of the day as a string.
814 """
815 return rpc_utils.get_motd()
816
817
mblighe8819cd2008-02-15 16:48:40 +0000818def get_static_data():
jadmanski0afbb632008-06-06 21:10:57 +0000819 """\
820 Returns a dictionary containing a bunch of data that shouldn't change
821 often and is otherwise inaccessible. This includes:
showardc92da832009-04-07 18:14:34 +0000822
823 priorities: List of job priority choices.
824 default_priority: Default priority value for new jobs.
825 users: Sorted list of all users.
826 labels: Sorted list of all labels.
827 atomic_groups: Sorted list of all atomic groups.
828 tests: Sorted list of all tests.
829 profilers: Sorted list of all profilers.
830 current_user: Logged-in username.
831 host_statuses: Sorted list of possible Host statuses.
832 job_statuses: Sorted list of possible HostQueueEntry statuses.
833 job_timeout_default: The default job timeout length in hours.
showarda1e74b32009-05-12 17:32:04 +0000834 parse_failed_repair_default: Default value for the parse_failed_repair job
835 option.
showardc92da832009-04-07 18:14:34 +0000836 reboot_before_options: A list of valid RebootBefore string enums.
837 reboot_after_options: A list of valid RebootAfter string enums.
838 motd: Server's message of the day.
839 status_dictionary: A mapping from one word job status names to a more
840 informative description.
jadmanski0afbb632008-06-06 21:10:57 +0000841 """
showard21baa452008-10-21 00:08:39 +0000842
843 job_fields = models.Job.get_field_dict()
jamesren76fcf192010-04-21 20:39:50 +0000844 default_drone_set_name = models.DroneSet.default_drone_set_name()
845 drone_sets = ([default_drone_set_name] +
846 sorted(drone_set.name for drone_set in
847 models.DroneSet.objects.exclude(
848 name=default_drone_set_name)))
showard21baa452008-10-21 00:08:39 +0000849
jadmanski0afbb632008-06-06 21:10:57 +0000850 result = {}
851 result['priorities'] = models.Job.Priority.choices()
showard21baa452008-10-21 00:08:39 +0000852 default_priority = job_fields['priority'].default
jadmanski0afbb632008-06-06 21:10:57 +0000853 default_string = models.Job.Priority.get_string(default_priority)
854 result['default_priority'] = default_string
855 result['users'] = get_users(sort_by=['login'])
856 result['labels'] = get_labels(sort_by=['-platform', 'name'])
showardc92da832009-04-07 18:14:34 +0000857 result['atomic_groups'] = get_atomic_groups(sort_by=['name'])
jadmanski0afbb632008-06-06 21:10:57 +0000858 result['tests'] = get_tests(sort_by=['name'])
showard2b9a88b2008-06-13 20:55:03 +0000859 result['profilers'] = get_profilers(sort_by=['name'])
showard0fc38302008-10-23 00:44:07 +0000860 result['current_user'] = rpc_utils.prepare_for_serialization(
showard64a95952010-01-13 21:27:16 +0000861 models.User.current_user().get_object_dict())
showard2b9a88b2008-06-13 20:55:03 +0000862 result['host_statuses'] = sorted(models.Host.Status.names)
mbligh5a198b92008-12-11 19:33:29 +0000863 result['job_statuses'] = sorted(models.HostQueueEntry.Status.names)
showardb1e51872008-10-07 11:08:18 +0000864 result['job_timeout_default'] = models.Job.DEFAULT_TIMEOUT
Simran Basi34217022012-11-06 13:43:15 -0800865 result['job_max_runtime_mins_default'] = (
866 models.Job.DEFAULT_MAX_RUNTIME_MINS)
showarda1e74b32009-05-12 17:32:04 +0000867 result['parse_failed_repair_default'] = bool(
868 models.Job.DEFAULT_PARSE_FAILED_REPAIR)
jamesrendd855242010-03-02 22:23:44 +0000869 result['reboot_before_options'] = model_attributes.RebootBefore.names
870 result['reboot_after_options'] = model_attributes.RebootAfter.names
showard8fbae652009-01-20 23:23:10 +0000871 result['motd'] = rpc_utils.get_motd()
jamesren76fcf192010-04-21 20:39:50 +0000872 result['drone_sets_enabled'] = models.DroneSet.drone_sets_enabled()
873 result['drone_sets'] = drone_sets
jamesren4a41e012010-07-16 22:33:48 +0000874 result['parameterized_jobs'] = models.Job.parameterized_jobs_enabled()
showard8ac29b42008-07-17 17:01:55 +0000875
showardd3dc1992009-04-22 21:01:40 +0000876 result['status_dictionary'] = {"Aborted": "Aborted",
showard8ac29b42008-07-17 17:01:55 +0000877 "Verifying": "Verifying Host",
878 "Pending": "Waiting on other hosts",
879 "Running": "Running autoserv",
880 "Completed": "Autoserv completed",
881 "Failed": "Failed to complete",
showardd823b362008-07-24 16:35:46 +0000882 "Queued": "Queued",
showard5deb6772008-11-04 21:54:33 +0000883 "Starting": "Next in host's queue",
884 "Stopped": "Other host(s) failed verify",
showardd3dc1992009-04-22 21:01:40 +0000885 "Parsing": "Awaiting parse of final results",
showard29f7cd22009-04-29 21:16:24 +0000886 "Gathering": "Gathering log files",
showard8cc058f2009-09-08 16:26:33 +0000887 "Template": "Template job for recurring run",
mbligh4608b002010-01-05 18:22:35 +0000888 "Waiting": "Waiting for scheduler action",
889 "Archiving": "Archiving results"}
jadmanski0afbb632008-06-06 21:10:57 +0000890 return result
showard29f7cd22009-04-29 21:16:24 +0000891
892
893def get_server_time():
894 return datetime.datetime.now().strftime("%Y-%m-%d %H:%M")