blob: 9c01d2cd8d81464ba0195142ae490b44aeb2acd3 [file] [log] [blame]
mblighe8819cd2008-02-15 16:48:40 +00001"""\
2Functions to expose over the RPC interface.
3
4For all modify* and delete* functions that ask for an 'id' parameter to
5identify the object to operate on, the id may be either
6 * the database row ID
7 * the name of the object (label name, hostname, user login, etc.)
8 * a dictionary containing uniquely identifying field (this option should seldom
9 be used)
10
11When specifying foreign key fields (i.e. adding hosts to a label, or adding
12users to an ACL group), the given value may be either the database row ID or the
13name of the object.
14
15All get* functions return lists of dictionaries. Each dictionary represents one
16object and maps field names to values.
17
18Some examples:
19modify_host(2, hostname='myhost') # modify hostname of host with database ID 2
20modify_host('ipaj2', hostname='myhost') # modify hostname of host 'ipaj2'
21modify_test('sleeptest', test_type='Client', params=', seconds=60')
22delete_acl_group(1) # delete by ID
23delete_acl_group('Everyone') # delete by name
24acl_group_add_users('Everyone', ['mbligh', 'showard'])
25get_jobs(owner='showard', status='Queued')
26
mbligh93c80e62009-02-03 17:48:30 +000027See doctests/001_rpc_test.txt for (lots) more examples.
mblighe8819cd2008-02-15 16:48:40 +000028"""
29
30__author__ = 'showard@google.com (Steve Howard)'
31
showard29f7cd22009-04-29 21:16:24 +000032import datetime
showardcafd16e2009-05-29 18:37:49 +000033import common
jamesrendd855242010-03-02 22:23:44 +000034from autotest_lib.frontend.afe import models, model_logic, model_attributes
showard6d7b2ff2009-06-10 00:16:47 +000035from autotest_lib.frontend.afe import control_file, rpc_utils
showard3bb499f2008-07-03 19:42:20 +000036from autotest_lib.client.common_lib import global_config
37
mblighe8819cd2008-02-15 16:48:40 +000038
Eric Lid23bc192011-02-09 14:38:57 -080039def get_parameterized_autoupdate_image_url(job):
40 """Get the parameterized autoupdate image url from a parameterized job."""
41 known_test_obj = models.Test.smart_get('autoupdate_ParameterizedJob')
42 image_parameter = known_test_obj.testparameter_set.get(test=known_test_obj,
43 name='image')
44 para_set = job.parameterized_job.parameterizedjobparameter_set
45 job_test_para = para_set.get(test_parameter=image_parameter)
46 return job_test_para.parameter_value
47
48
mblighe8819cd2008-02-15 16:48:40 +000049# labels
50
showard989f25d2008-10-01 11:38:11 +000051def add_label(name, kernel_config=None, platform=None, only_if_needed=None):
showardc92da832009-04-07 18:14:34 +000052 return models.Label.add_object(
53 name=name, kernel_config=kernel_config, platform=platform,
54 only_if_needed=only_if_needed).id
mblighe8819cd2008-02-15 16:48:40 +000055
56
57def modify_label(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +000058 models.Label.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +000059
60
61def delete_label(id):
jadmanski0afbb632008-06-06 21:10:57 +000062 models.Label.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +000063
64
showardbbabf502008-06-06 00:02:02 +000065def label_add_hosts(id, hosts):
showardbe3ec042008-11-12 18:16:07 +000066 host_objs = models.Host.smart_get_bulk(hosts)
showardcafd16e2009-05-29 18:37:49 +000067 label = models.Label.smart_get(id)
68 if label.platform:
69 models.Host.check_no_platform(host_objs)
70 label.host_set.add(*host_objs)
showardbbabf502008-06-06 00:02:02 +000071
72
73def label_remove_hosts(id, hosts):
showardbe3ec042008-11-12 18:16:07 +000074 host_objs = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +000075 models.Label.smart_get(id).host_set.remove(*host_objs)
showardbbabf502008-06-06 00:02:02 +000076
77
mblighe8819cd2008-02-15 16:48:40 +000078def get_labels(**filter_data):
showardc92da832009-04-07 18:14:34 +000079 """\
80 @returns A sequence of nested dictionaries of label information.
81 """
82 return rpc_utils.prepare_rows_as_nested_dicts(
83 models.Label.query_objects(filter_data),
84 ('atomic_group',))
85
86
87# atomic groups
88
showarde9450c92009-06-30 01:58:52 +000089def add_atomic_group(name, max_number_of_machines=None, description=None):
showardc92da832009-04-07 18:14:34 +000090 return models.AtomicGroup.add_object(
91 name=name, max_number_of_machines=max_number_of_machines,
92 description=description).id
93
94
95def modify_atomic_group(id, **data):
96 models.AtomicGroup.smart_get(id).update_object(data)
97
98
99def delete_atomic_group(id):
100 models.AtomicGroup.smart_get(id).delete()
101
102
103def atomic_group_add_labels(id, labels):
104 label_objs = models.Label.smart_get_bulk(labels)
105 models.AtomicGroup.smart_get(id).label_set.add(*label_objs)
106
107
108def atomic_group_remove_labels(id, labels):
109 label_objs = models.Label.smart_get_bulk(labels)
110 models.AtomicGroup.smart_get(id).label_set.remove(*label_objs)
111
112
113def get_atomic_groups(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000114 return rpc_utils.prepare_for_serialization(
showardc92da832009-04-07 18:14:34 +0000115 models.AtomicGroup.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000116
117
118# hosts
119
showarddf062562008-07-03 19:56:37 +0000120def add_host(hostname, status=None, locked=None, protection=None):
jadmanski0afbb632008-06-06 21:10:57 +0000121 return models.Host.add_object(hostname=hostname, status=status,
showarddf062562008-07-03 19:56:37 +0000122 locked=locked, protection=protection).id
mblighe8819cd2008-02-15 16:48:40 +0000123
124
125def modify_host(id, **data):
showardbe0d8692009-08-20 23:42:44 +0000126 rpc_utils.check_modify_host(data)
showardce7c0922009-09-11 18:39:24 +0000127 host = models.Host.smart_get(id)
128 rpc_utils.check_modify_host_locking(host, data)
129 host.update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000130
131
showard276f9442009-05-20 00:33:16 +0000132def modify_hosts(host_filter_data, update_data):
133 """
showardbe0d8692009-08-20 23:42:44 +0000134 @param host_filter_data: Filters out which hosts to modify.
135 @param update_data: A dictionary with the changes to make to the hosts.
showard276f9442009-05-20 00:33:16 +0000136 """
showardbe0d8692009-08-20 23:42:44 +0000137 rpc_utils.check_modify_host(update_data)
showard276f9442009-05-20 00:33:16 +0000138 hosts = models.Host.query_objects(host_filter_data)
139 for host in hosts:
140 host.update_object(update_data)
141
142
mblighe8819cd2008-02-15 16:48:40 +0000143def host_add_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000144 labels = models.Label.smart_get_bulk(labels)
showardcafd16e2009-05-29 18:37:49 +0000145 host = models.Host.smart_get(id)
146
147 platforms = [label.name for label in labels if label.platform]
148 if len(platforms) > 1:
149 raise model_logic.ValidationError(
150 {'labels': 'Adding more than one platform label: %s' %
151 ', '.join(platforms)})
152 if len(platforms) == 1:
153 models.Host.check_no_platform([host])
154 host.labels.add(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000155
156
157def host_remove_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000158 labels = models.Label.smart_get_bulk(labels)
jadmanski0afbb632008-06-06 21:10:57 +0000159 models.Host.smart_get(id).labels.remove(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000160
161
showard0957a842009-05-11 19:25:08 +0000162def set_host_attribute(attribute, value, **host_filter_data):
163 """
164 @param attribute string name of attribute
165 @param value string, or None to delete an attribute
166 @param host_filter_data filter data to apply to Hosts to choose hosts to act
167 upon
168 """
169 assert host_filter_data # disallow accidental actions on all hosts
170 hosts = models.Host.query_objects(host_filter_data)
171 models.AclGroup.check_for_acl_violation_hosts(hosts)
172
173 for host in hosts:
showardf8b19042009-05-12 17:22:49 +0000174 host.set_or_delete_attribute(attribute, value)
showard0957a842009-05-11 19:25:08 +0000175
176
mblighe8819cd2008-02-15 16:48:40 +0000177def delete_host(id):
jadmanski0afbb632008-06-06 21:10:57 +0000178 models.Host.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000179
180
showard87cc38f2009-08-20 23:37:04 +0000181def get_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000182 exclude_atomic_group_hosts=False, valid_only=True, **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000183 """
184 @param multiple_labels: match hosts in all of the labels given. Should
185 be a list of label names.
186 @param exclude_only_if_needed_labels: Exclude hosts with at least one
187 "only_if_needed" label applied.
188 @param exclude_atomic_group_hosts: Exclude hosts that have one or more
189 atomic group labels associated with them.
jadmanski0afbb632008-06-06 21:10:57 +0000190 """
showard43a3d262008-11-12 18:17:05 +0000191 hosts = rpc_utils.get_host_query(multiple_labels,
192 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000193 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000194 valid_only, filter_data)
showard0957a842009-05-11 19:25:08 +0000195 hosts = list(hosts)
196 models.Host.objects.populate_relationships(hosts, models.Label,
197 'label_list')
198 models.Host.objects.populate_relationships(hosts, models.AclGroup,
199 'acl_list')
200 models.Host.objects.populate_relationships(hosts, models.HostAttribute,
201 'attribute_list')
showard43a3d262008-11-12 18:17:05 +0000202 host_dicts = []
203 for host_obj in hosts:
204 host_dict = host_obj.get_object_dict()
showard0957a842009-05-11 19:25:08 +0000205 host_dict['labels'] = [label.name for label in host_obj.label_list]
showard909c9142009-07-07 20:54:42 +0000206 host_dict['platform'], host_dict['atomic_group'] = (rpc_utils.
207 find_platform_and_atomic_group(host_obj))
showard0957a842009-05-11 19:25:08 +0000208 host_dict['acls'] = [acl.name for acl in host_obj.acl_list]
209 host_dict['attributes'] = dict((attribute.attribute, attribute.value)
210 for attribute in host_obj.attribute_list)
showard43a3d262008-11-12 18:17:05 +0000211 host_dicts.append(host_dict)
212 return rpc_utils.prepare_for_serialization(host_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000213
214
showard87cc38f2009-08-20 23:37:04 +0000215def get_num_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000216 exclude_atomic_group_hosts=False, valid_only=True,
217 **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000218 """
219 Same parameters as get_hosts().
220
221 @returns The number of matching hosts.
222 """
showard43a3d262008-11-12 18:17:05 +0000223 hosts = rpc_utils.get_host_query(multiple_labels,
224 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000225 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000226 valid_only, filter_data)
showard43a3d262008-11-12 18:17:05 +0000227 return hosts.count()
showard1385b162008-03-13 15:59:40 +0000228
mblighe8819cd2008-02-15 16:48:40 +0000229
230# tests
231
showard909c7a62008-07-15 21:52:38 +0000232def add_test(name, test_type, path, author=None, dependencies=None,
showard3d9899a2008-07-31 02:11:58 +0000233 experimental=True, run_verify=None, test_class=None,
showard909c7a62008-07-15 21:52:38 +0000234 test_time=None, test_category=None, description=None,
235 sync_count=1):
jadmanski0afbb632008-06-06 21:10:57 +0000236 return models.Test.add_object(name=name, test_type=test_type, path=path,
showard909c7a62008-07-15 21:52:38 +0000237 author=author, dependencies=dependencies,
238 experimental=experimental,
239 run_verify=run_verify, test_time=test_time,
240 test_category=test_category,
241 sync_count=sync_count,
jadmanski0afbb632008-06-06 21:10:57 +0000242 test_class=test_class,
243 description=description).id
mblighe8819cd2008-02-15 16:48:40 +0000244
245
246def modify_test(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000247 models.Test.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000248
249
250def delete_test(id):
jadmanski0afbb632008-06-06 21:10:57 +0000251 models.Test.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000252
253
254def get_tests(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000255 return rpc_utils.prepare_for_serialization(
256 models.Test.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000257
258
showard2b9a88b2008-06-13 20:55:03 +0000259# profilers
260
261def add_profiler(name, description=None):
262 return models.Profiler.add_object(name=name, description=description).id
263
264
265def modify_profiler(id, **data):
266 models.Profiler.smart_get(id).update_object(data)
267
268
269def delete_profiler(id):
270 models.Profiler.smart_get(id).delete()
271
272
273def get_profilers(**filter_data):
274 return rpc_utils.prepare_for_serialization(
275 models.Profiler.list_objects(filter_data))
276
277
mblighe8819cd2008-02-15 16:48:40 +0000278# users
279
280def add_user(login, access_level=None):
jadmanski0afbb632008-06-06 21:10:57 +0000281 return models.User.add_object(login=login, access_level=access_level).id
mblighe8819cd2008-02-15 16:48:40 +0000282
283
284def modify_user(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000285 models.User.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000286
287
288def delete_user(id):
jadmanski0afbb632008-06-06 21:10:57 +0000289 models.User.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000290
291
292def get_users(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000293 return rpc_utils.prepare_for_serialization(
294 models.User.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000295
296
297# acl groups
298
299def add_acl_group(name, description=None):
showard04f2cd82008-07-25 20:53:31 +0000300 group = models.AclGroup.add_object(name=name, description=description)
showard64a95952010-01-13 21:27:16 +0000301 group.users.add(models.User.current_user())
showard04f2cd82008-07-25 20:53:31 +0000302 return group.id
mblighe8819cd2008-02-15 16:48:40 +0000303
304
305def modify_acl_group(id, **data):
showard04f2cd82008-07-25 20:53:31 +0000306 group = models.AclGroup.smart_get(id)
307 group.check_for_acl_violation_acl_group()
308 group.update_object(data)
309 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000310
311
312def acl_group_add_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000313 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000314 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000315 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000316 group.users.add(*users)
mblighe8819cd2008-02-15 16:48:40 +0000317
318
319def acl_group_remove_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000320 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000321 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000322 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000323 group.users.remove(*users)
showard04f2cd82008-07-25 20:53:31 +0000324 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000325
326
327def acl_group_add_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000328 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000329 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000330 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000331 group.hosts.add(*hosts)
showard08f981b2008-06-24 21:59:03 +0000332 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000333
334
335def acl_group_remove_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000336 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000337 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000338 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000339 group.hosts.remove(*hosts)
showard08f981b2008-06-24 21:59:03 +0000340 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000341
342
343def delete_acl_group(id):
jadmanski0afbb632008-06-06 21:10:57 +0000344 models.AclGroup.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000345
346
347def get_acl_groups(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000348 acl_groups = models.AclGroup.list_objects(filter_data)
349 for acl_group in acl_groups:
350 acl_group_obj = models.AclGroup.objects.get(id=acl_group['id'])
351 acl_group['users'] = [user.login
352 for user in acl_group_obj.users.all()]
353 acl_group['hosts'] = [host.hostname
354 for host in acl_group_obj.hosts.all()]
355 return rpc_utils.prepare_for_serialization(acl_groups)
mblighe8819cd2008-02-15 16:48:40 +0000356
357
358# jobs
359
mbligh120351e2009-01-24 01:40:45 +0000360def generate_control_file(tests=(), kernel=None, label=None, profilers=(),
showard91f85102009-10-12 20:34:52 +0000361 client_control_file='', use_container=False,
showard232b7ae2009-11-10 00:46:48 +0000362 profile_only=None, upload_kernel_config=False):
jadmanski0afbb632008-06-06 21:10:57 +0000363 """
mbligh120351e2009-01-24 01:40:45 +0000364 Generates a client-side control file to load a kernel and run tests.
365
366 @param tests List of tests to run.
mbligha3c58d22009-08-24 22:01:51 +0000367 @param kernel A list of kernel info dictionaries configuring which kernels
368 to boot for this job and other options for them
mbligh120351e2009-01-24 01:40:45 +0000369 @param label Name of label to grab kernel config from.
370 @param profilers List of profilers to activate during the job.
371 @param client_control_file The contents of a client-side control file to
372 run at the end of all tests. If this is supplied, all tests must be
373 client side.
374 TODO: in the future we should support server control files directly
375 to wrap with a kernel. That'll require changing the parameter
376 name and adding a boolean to indicate if it is a client or server
377 control file.
378 @param use_container unused argument today. TODO: Enable containers
379 on the host during a client side test.
showard91f85102009-10-12 20:34:52 +0000380 @param profile_only A boolean that indicates what default profile_only
381 mode to use in the control file. Passing None will generate a
382 control file that does not explcitly set the default mode at all.
showard232b7ae2009-11-10 00:46:48 +0000383 @param upload_kernel_config: if enabled it will generate server control
384 file code that uploads the kernel config file to the client and
385 tells the client of the new (local) path when compiling the kernel;
386 the tests must be server side tests
mbligh120351e2009-01-24 01:40:45 +0000387
388 @returns a dict with the following keys:
389 control_file: str, The control file text.
390 is_server: bool, is the control file a server-side control file?
391 synch_count: How many machines the job uses per autoserv execution.
392 synch_count == 1 means the job is asynchronous.
393 dependencies: A list of the names of labels on which the job depends.
394 """
showardd86debe2009-06-10 17:37:56 +0000395 if not tests and not client_control_file:
showard2bab8f42008-11-12 18:15:22 +0000396 return dict(control_file='', is_server=False, synch_count=1,
showard989f25d2008-10-01 11:38:11 +0000397 dependencies=[])
mblighe8819cd2008-02-15 16:48:40 +0000398
showard989f25d2008-10-01 11:38:11 +0000399 cf_info, test_objects, profiler_objects, label = (
showard2b9a88b2008-06-13 20:55:03 +0000400 rpc_utils.prepare_generate_control_file(tests, kernel, label,
401 profilers))
showard989f25d2008-10-01 11:38:11 +0000402 cf_info['control_file'] = control_file.generate_control(
mbligha3c58d22009-08-24 22:01:51 +0000403 tests=test_objects, kernels=kernel, platform=label,
mbligh120351e2009-01-24 01:40:45 +0000404 profilers=profiler_objects, is_server=cf_info['is_server'],
showard232b7ae2009-11-10 00:46:48 +0000405 client_control_file=client_control_file, profile_only=profile_only,
406 upload_kernel_config=upload_kernel_config)
showard989f25d2008-10-01 11:38:11 +0000407 return cf_info
mblighe8819cd2008-02-15 16:48:40 +0000408
409
jamesren4a41e012010-07-16 22:33:48 +0000410def create_parameterized_job(name, priority, test, parameters, kernel=None,
411 label=None, profilers=(), profiler_parameters=None,
412 use_container=False, profile_only=None,
413 upload_kernel_config=False, hosts=(),
414 meta_hosts=(), one_time_hosts=(),
415 atomic_group_name=None, synch_count=None,
416 is_template=False, timeout=None,
Scott Zawalskia8a0d752012-11-08 14:13:43 -0800417 max_runtime_hrs=None, run_verify=True,
jamesren4a41e012010-07-16 22:33:48 +0000418 email_list='', dependencies=(), reboot_before=None,
419 reboot_after=None, parse_failed_repair=None,
420 hostless=False, keyvals=None, drone_set=None):
421 """
422 Creates and enqueues a parameterized job.
423
424 Most parameters a combination of the parameters for generate_control_file()
425 and create_job(), with the exception of:
426
427 @param test name or ID of the test to run
428 @param parameters a map of parameter name ->
429 tuple of (param value, param type)
430 @param profiler_parameters a dictionary of parameters for the profilers:
431 key: profiler name
432 value: dict of param name -> tuple of
433 (param value,
434 param type)
435 """
436 # Save the values of the passed arguments here. What we're going to do with
437 # them is pass them all to rpc_utils.get_create_job_common_args(), which
438 # will extract the subset of these arguments that apply for
439 # rpc_utils.create_job_common(), which we then pass in to that function.
440 args = locals()
441
442 # Set up the parameterized job configs
443 test_obj = models.Test.smart_get(test)
444 if test_obj.test_type == model_attributes.TestTypes.SERVER:
445 control_type = models.Job.ControlType.SERVER
446 else:
447 control_type = models.Job.ControlType.CLIENT
448
449 try:
450 label = models.Label.smart_get(label)
451 except models.Label.DoesNotExist:
452 label = None
453
454 kernel_objs = models.Kernel.create_kernels(kernel)
455 profiler_objs = [models.Profiler.smart_get(profiler)
456 for profiler in profilers]
457
458 parameterized_job = models.ParameterizedJob.objects.create(
459 test=test_obj, label=label, use_container=use_container,
460 profile_only=profile_only,
461 upload_kernel_config=upload_kernel_config)
462 parameterized_job.kernels.add(*kernel_objs)
463
464 for profiler in profiler_objs:
465 parameterized_profiler = models.ParameterizedJobProfiler.objects.create(
466 parameterized_job=parameterized_job,
467 profiler=profiler)
468 profiler_params = profiler_parameters.get(profiler.name, {})
469 for name, (value, param_type) in profiler_params.iteritems():
470 models.ParameterizedJobProfilerParameter.objects.create(
471 parameterized_job_profiler=parameterized_profiler,
472 parameter_name=name,
473 parameter_value=value,
474 parameter_type=param_type)
475
476 try:
477 for parameter in test_obj.testparameter_set.all():
478 if parameter.name in parameters:
479 param_value, param_type = parameters.pop(parameter.name)
480 parameterized_job.parameterizedjobparameter_set.create(
481 test_parameter=parameter, parameter_value=param_value,
482 parameter_type=param_type)
483
484 if parameters:
485 raise Exception('Extra parameters remain: %r' % parameters)
486
487 return rpc_utils.create_job_common(
488 parameterized_job=parameterized_job.id,
489 control_type=control_type,
490 **rpc_utils.get_create_job_common_args(args))
491 except:
492 parameterized_job.delete()
493 raise
494
495
showard12f3e322009-05-13 21:27:42 +0000496def create_job(name, priority, control_file, control_type,
497 hosts=(), meta_hosts=(), one_time_hosts=(),
498 atomic_group_name=None, synch_count=None, is_template=False,
Scott Zawalskia8a0d752012-11-08 14:13:43 -0800499 timeout=None, max_runtime_hrs=None, run_verify=True,
showard12f3e322009-05-13 21:27:42 +0000500 email_list='', dependencies=(), reboot_before=None,
showardc1a98d12010-01-15 00:22:22 +0000501 reboot_after=None, parse_failed_repair=None, hostless=False,
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800502 keyvals=None, drone_set=None, image=None):
jadmanski0afbb632008-06-06 21:10:57 +0000503 """\
504 Create and enqueue a job.
mblighe8819cd2008-02-15 16:48:40 +0000505
showarda1e74b32009-05-12 17:32:04 +0000506 @param name name of this job
507 @param priority Low, Medium, High, Urgent
508 @param control_file String contents of the control file.
509 @param control_type Type of control file, Client or Server.
510 @param synch_count How many machines the job uses per autoserv execution.
511 synch_count == 1 means the job is asynchronous. If an atomic group is
512 given this value is treated as a minimum.
513 @param is_template If true then create a template job.
514 @param timeout Hours after this call returns until the job times out.
Scott Zawalskia8a0d752012-11-08 14:13:43 -0800515 @param max_runtime_hrs Hours from job starting time until job times out
showarda1e74b32009-05-12 17:32:04 +0000516 @param run_verify Should the host be verified before running the test?
517 @param email_list String containing emails to mail when the job is done
518 @param dependencies List of label names on which this job depends
519 @param reboot_before Never, If dirty, or Always
520 @param reboot_after Never, If all tests passed, or Always
521 @param parse_failed_repair if true, results of failed repairs launched by
522 this job will be parsed as part of the job.
showarda9545c02009-12-18 22:44:26 +0000523 @param hostless if true, create a hostless job
showardc1a98d12010-01-15 00:22:22 +0000524 @param keyvals dict of keyvals to associate with the job
showarda1e74b32009-05-12 17:32:04 +0000525
526 @param hosts List of hosts to run job on.
527 @param meta_hosts List where each entry is a label name, and for each entry
528 one host will be chosen from that label to run the job on.
529 @param one_time_hosts List of hosts not in the database to run the job on.
530 @param atomic_group_name The name of an atomic group to schedule the job on.
jamesren76fcf192010-04-21 20:39:50 +0000531 @param drone_set The name of the drone set to run this test on.
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800532 @param image OS image to install before running job.
showarda1e74b32009-05-12 17:32:04 +0000533
showardc92da832009-04-07 18:14:34 +0000534
535 @returns The created Job id number.
jadmanski0afbb632008-06-06 21:10:57 +0000536 """
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800537
538 if image is None:
539 return rpc_utils.create_job_common(
540 **rpc_utils.get_create_job_common_args(locals()))
541
542 # When image is supplied use a known parameterized test already in the
543 # database to pass the OS image path from the front end, through the
544 # scheduler, and finally to autoserv as the --image parameter.
545
546 # The test autoupdate_ParameterizedJob is in afe_autotests and used to
547 # instantiate a Test object and from there a ParameterizedJob.
548 known_test_obj = models.Test.smart_get('autoupdate_ParameterizedJob')
549 known_parameterized_job = models.ParameterizedJob.objects.create(
550 test=known_test_obj)
551
552 # autoupdate_ParameterizedJob has a single parameter, the image parameter,
553 # stored in the table afe_test_parameters. We retrieve and set this
554 # instance of the parameter to the OS image path.
Eric Lid23bc192011-02-09 14:38:57 -0800555 image_parameter = known_test_obj.testparameter_set.get(test=known_test_obj,
556 name='image')
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800557 known_parameterized_job.parameterizedjobparameter_set.create(
558 test_parameter=image_parameter, parameter_value=image,
559 parameter_type='string')
560
561 # By passing a parameterized_job to create_job_common the job entry in
562 # the afe_jobs table will have the field parameterized_job_id set.
563 # The scheduler uses this id in the afe_parameterized_jobs table to
564 # match this job to our known test, and then with the
565 # afe_parameterized_job_parameters table to get the actual image path.
jamesren4a41e012010-07-16 22:33:48 +0000566 return rpc_utils.create_job_common(
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800567 parameterized_job=known_parameterized_job.id,
jamesren4a41e012010-07-16 22:33:48 +0000568 **rpc_utils.get_create_job_common_args(locals()))
mblighe8819cd2008-02-15 16:48:40 +0000569
570
showard9dbdcda2008-10-14 17:34:36 +0000571def abort_host_queue_entries(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000572 """\
showard9dbdcda2008-10-14 17:34:36 +0000573 Abort a set of host queue entries.
jadmanski0afbb632008-06-06 21:10:57 +0000574 """
showard9dbdcda2008-10-14 17:34:36 +0000575 query = models.HostQueueEntry.query_objects(filter_data)
showard0c185192009-01-16 03:07:57 +0000576 query = query.filter(complete=False)
showarddc817512008-11-12 18:16:41 +0000577 models.AclGroup.check_abort_permissions(query)
showard9dbdcda2008-10-14 17:34:36 +0000578 host_queue_entries = list(query.select_related())
showard2bab8f42008-11-12 18:15:22 +0000579 rpc_utils.check_abort_synchronous_jobs(host_queue_entries)
mblighe8819cd2008-02-15 16:48:40 +0000580
showard9dbdcda2008-10-14 17:34:36 +0000581 for queue_entry in host_queue_entries:
showard64a95952010-01-13 21:27:16 +0000582 queue_entry.abort()
showard9d821ab2008-07-11 16:54:29 +0000583
584
showard1ff7b2e2009-05-15 23:17:18 +0000585def reverify_hosts(**filter_data):
586 """\
587 Schedules a set of hosts for verify.
mbligh4e545a52009-12-19 05:30:39 +0000588
589 @returns A list of hostnames that a verify task was created for.
showard1ff7b2e2009-05-15 23:17:18 +0000590 """
591 hosts = models.Host.query_objects(filter_data)
592 models.AclGroup.check_for_acl_violation_hosts(hosts)
showardc5103442010-01-15 00:20:26 +0000593 for host in hosts:
594 models.SpecialTask.schedule_special_task(host,
595 models.SpecialTask.Task.VERIFY)
mbligh4e545a52009-12-19 05:30:39 +0000596 return list(sorted(host.hostname for host in hosts))
showard1ff7b2e2009-05-15 23:17:18 +0000597
598
mblighe8819cd2008-02-15 16:48:40 +0000599def get_jobs(not_yet_run=False, running=False, finished=False, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000600 """\
601 Extra filter args for get_jobs:
602 -not_yet_run: Include only jobs that have not yet started running.
603 -running: Include only jobs that have start running but for which not
604 all hosts have completed.
605 -finished: Include only jobs for which all hosts have completed (or
606 aborted).
607 At most one of these three fields should be specified.
608 """
609 filter_data['extra_args'] = rpc_utils.extra_job_filters(not_yet_run,
610 running,
611 finished)
showard0957a842009-05-11 19:25:08 +0000612 job_dicts = []
613 jobs = list(models.Job.query_objects(filter_data))
614 models.Job.objects.populate_relationships(jobs, models.Label,
615 'dependencies')
showardc1a98d12010-01-15 00:22:22 +0000616 models.Job.objects.populate_relationships(jobs, models.JobKeyval, 'keyvals')
showard0957a842009-05-11 19:25:08 +0000617 for job in jobs:
618 job_dict = job.get_object_dict()
619 job_dict['dependencies'] = ','.join(label.name
620 for label in job.dependencies)
showardc1a98d12010-01-15 00:22:22 +0000621 job_dict['keyvals'] = dict((keyval.key, keyval.value)
622 for keyval in job.keyvals)
Eric Lid23bc192011-02-09 14:38:57 -0800623 if job.parameterized_job:
624 job_dict['image'] = get_parameterized_autoupdate_image_url(job)
showard0957a842009-05-11 19:25:08 +0000625 job_dicts.append(job_dict)
626 return rpc_utils.prepare_for_serialization(job_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000627
628
629def get_num_jobs(not_yet_run=False, running=False, finished=False,
jadmanski0afbb632008-06-06 21:10:57 +0000630 **filter_data):
631 """\
632 See get_jobs() for documentation of extra filter parameters.
633 """
634 filter_data['extra_args'] = rpc_utils.extra_job_filters(not_yet_run,
635 running,
636 finished)
637 return models.Job.query_count(filter_data)
mblighe8819cd2008-02-15 16:48:40 +0000638
639
mblighe8819cd2008-02-15 16:48:40 +0000640def get_jobs_summary(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000641 """\
showarda8709c52008-07-03 19:44:54 +0000642 Like get_jobs(), but adds a 'status_counts' field, which is a dictionary
jadmanski0afbb632008-06-06 21:10:57 +0000643 mapping status strings to the number of hosts currently with that
644 status, i.e. {'Queued' : 4, 'Running' : 2}.
645 """
646 jobs = get_jobs(**filter_data)
647 ids = [job['id'] for job in jobs]
648 all_status_counts = models.Job.objects.get_status_counts(ids)
649 for job in jobs:
650 job['status_counts'] = all_status_counts[job['id']]
651 return rpc_utils.prepare_for_serialization(jobs)
mblighe8819cd2008-02-15 16:48:40 +0000652
653
showarda965cef2009-05-15 23:17:41 +0000654def get_info_for_clone(id, preserve_metahosts, queue_entry_filter_data=None):
showarda8709c52008-07-03 19:44:54 +0000655 """\
656 Retrieves all the information needed to clone a job.
657 """
showarda8709c52008-07-03 19:44:54 +0000658 job = models.Job.objects.get(id=id)
showard29f7cd22009-04-29 21:16:24 +0000659 job_info = rpc_utils.get_job_info(job,
showarda965cef2009-05-15 23:17:41 +0000660 preserve_metahosts,
661 queue_entry_filter_data)
showard945072f2008-09-03 20:34:59 +0000662
showardd9992fe2008-07-31 02:15:03 +0000663 host_dicts = []
showard29f7cd22009-04-29 21:16:24 +0000664 for host in job_info['hosts']:
665 host_dict = get_hosts(id=host.id)[0]
666 other_labels = host_dict['labels']
667 if host_dict['platform']:
668 other_labels.remove(host_dict['platform'])
669 host_dict['other_labels'] = ', '.join(other_labels)
showardd9992fe2008-07-31 02:15:03 +0000670 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +0000671
showard29f7cd22009-04-29 21:16:24 +0000672 for host in job_info['one_time_hosts']:
673 host_dict = dict(hostname=host.hostname,
674 id=host.id,
675 platform='(one-time host)',
676 locked_text='')
677 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +0000678
showard4d077562009-05-08 18:24:36 +0000679 # convert keys from Label objects to strings (names of labels)
showard29f7cd22009-04-29 21:16:24 +0000680 meta_host_counts = dict((meta_host.name, count) for meta_host, count
showard4d077562009-05-08 18:24:36 +0000681 in job_info['meta_host_counts'].iteritems())
showard29f7cd22009-04-29 21:16:24 +0000682
683 info = dict(job=job.get_object_dict(),
684 meta_host_counts=meta_host_counts,
685 hosts=host_dicts)
686 info['job']['dependencies'] = job_info['dependencies']
687 if job_info['atomic_group']:
688 info['atomic_group_name'] = (job_info['atomic_group']).name
689 else:
690 info['atomic_group_name'] = None
jamesren2275ef12010-04-12 18:25:06 +0000691 info['hostless'] = job_info['hostless']
jamesren76fcf192010-04-21 20:39:50 +0000692 info['drone_set'] = job.drone_set and job.drone_set.name
showarda8709c52008-07-03 19:44:54 +0000693
Eric Lid23bc192011-02-09 14:38:57 -0800694 if job.parameterized_job:
695 info['job']['image'] = get_parameterized_autoupdate_image_url(job)
696
showarda8709c52008-07-03 19:44:54 +0000697 return rpc_utils.prepare_for_serialization(info)
698
699
showard34dc5fa2008-04-24 20:58:40 +0000700# host queue entries
701
702def get_host_queue_entries(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000703 """\
showardc92da832009-04-07 18:14:34 +0000704 @returns A sequence of nested dictionaries of host and job information.
jadmanski0afbb632008-06-06 21:10:57 +0000705 """
showardc92da832009-04-07 18:14:34 +0000706 return rpc_utils.prepare_rows_as_nested_dicts(
707 models.HostQueueEntry.query_objects(filter_data),
708 ('host', 'atomic_group', 'job'))
showard34dc5fa2008-04-24 20:58:40 +0000709
710
711def get_num_host_queue_entries(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000712 """\
713 Get the number of host queue entries associated with this job.
714 """
715 return models.HostQueueEntry.query_count(filter_data)
showard34dc5fa2008-04-24 20:58:40 +0000716
717
showard1e935f12008-07-11 00:11:36 +0000718def get_hqe_percentage_complete(**filter_data):
719 """
showardc92da832009-04-07 18:14:34 +0000720 Computes the fraction of host queue entries matching the given filter data
showard1e935f12008-07-11 00:11:36 +0000721 that are complete.
722 """
723 query = models.HostQueueEntry.query_objects(filter_data)
724 complete_count = query.filter(complete=True).count()
725 total_count = query.count()
726 if total_count == 0:
727 return 1
728 return float(complete_count) / total_count
729
730
showard1a5a4082009-07-28 20:01:37 +0000731# special tasks
732
733def get_special_tasks(**filter_data):
734 return rpc_utils.prepare_rows_as_nested_dicts(
735 models.SpecialTask.query_objects(filter_data),
736 ('host', 'queue_entry'))
737
738
showardc0ac3a72009-07-08 21:14:45 +0000739# support for host detail view
740
741def get_host_queue_entries_and_special_tasks(hostname, query_start=None,
742 query_limit=None):
743 """
744 @returns an interleaved list of HostQueueEntries and SpecialTasks,
745 in approximate run order. each dict contains keys for type, host,
746 job, status, started_on, execution_path, and ID.
747 """
748 total_limit = None
749 if query_limit is not None:
750 total_limit = query_start + query_limit
751 filter_data = {'host__hostname': hostname,
752 'query_limit': total_limit,
753 'sort_by': ['-id']}
754
755 queue_entries = list(models.HostQueueEntry.query_objects(filter_data))
756 special_tasks = list(models.SpecialTask.query_objects(filter_data))
757
758 interleaved_entries = rpc_utils.interleave_entries(queue_entries,
759 special_tasks)
760 if query_start is not None:
761 interleaved_entries = interleaved_entries[query_start:]
762 if query_limit is not None:
763 interleaved_entries = interleaved_entries[:query_limit]
764 return rpc_utils.prepare_for_serialization(interleaved_entries)
765
766
767def get_num_host_queue_entries_and_special_tasks(hostname):
768 filter_data = {'host__hostname': hostname}
769 return (models.HostQueueEntry.query_count(filter_data)
770 + models.SpecialTask.query_count(filter_data))
771
772
showard29f7cd22009-04-29 21:16:24 +0000773# recurring run
774
775def get_recurring(**filter_data):
776 return rpc_utils.prepare_rows_as_nested_dicts(
777 models.RecurringRun.query_objects(filter_data),
778 ('job', 'owner'))
779
780
781def get_num_recurring(**filter_data):
782 return models.RecurringRun.query_count(filter_data)
783
784
785def delete_recurring_runs(**filter_data):
786 to_delete = models.RecurringRun.query_objects(filter_data)
787 to_delete.delete()
788
789
790def create_recurring_run(job_id, start_date, loop_period, loop_count):
showard64a95952010-01-13 21:27:16 +0000791 owner = models.User.current_user().login
showard29f7cd22009-04-29 21:16:24 +0000792 job = models.Job.objects.get(id=job_id)
793 return job.create_recurring_job(start_date=start_date,
794 loop_period=loop_period,
795 loop_count=loop_count,
796 owner=owner)
797
798
mblighe8819cd2008-02-15 16:48:40 +0000799# other
800
showarde0b63622008-08-04 20:58:47 +0000801def echo(data=""):
802 """\
803 Returns a passed in string. For doing a basic test to see if RPC calls
804 can successfully be made.
805 """
806 return data
807
808
showardb7a52fd2009-04-27 20:10:56 +0000809def get_motd():
810 """\
811 Returns the message of the day as a string.
812 """
813 return rpc_utils.get_motd()
814
815
mblighe8819cd2008-02-15 16:48:40 +0000816def get_static_data():
jadmanski0afbb632008-06-06 21:10:57 +0000817 """\
818 Returns a dictionary containing a bunch of data that shouldn't change
819 often and is otherwise inaccessible. This includes:
showardc92da832009-04-07 18:14:34 +0000820
821 priorities: List of job priority choices.
822 default_priority: Default priority value for new jobs.
823 users: Sorted list of all users.
824 labels: Sorted list of all labels.
825 atomic_groups: Sorted list of all atomic groups.
826 tests: Sorted list of all tests.
827 profilers: Sorted list of all profilers.
828 current_user: Logged-in username.
829 host_statuses: Sorted list of possible Host statuses.
830 job_statuses: Sorted list of possible HostQueueEntry statuses.
831 job_timeout_default: The default job timeout length in hours.
showarda1e74b32009-05-12 17:32:04 +0000832 parse_failed_repair_default: Default value for the parse_failed_repair job
833 option.
showardc92da832009-04-07 18:14:34 +0000834 reboot_before_options: A list of valid RebootBefore string enums.
835 reboot_after_options: A list of valid RebootAfter string enums.
836 motd: Server's message of the day.
837 status_dictionary: A mapping from one word job status names to a more
838 informative description.
jadmanski0afbb632008-06-06 21:10:57 +0000839 """
showard21baa452008-10-21 00:08:39 +0000840
841 job_fields = models.Job.get_field_dict()
jamesren76fcf192010-04-21 20:39:50 +0000842 default_drone_set_name = models.DroneSet.default_drone_set_name()
843 drone_sets = ([default_drone_set_name] +
844 sorted(drone_set.name for drone_set in
845 models.DroneSet.objects.exclude(
846 name=default_drone_set_name)))
showard21baa452008-10-21 00:08:39 +0000847
jadmanski0afbb632008-06-06 21:10:57 +0000848 result = {}
849 result['priorities'] = models.Job.Priority.choices()
showard21baa452008-10-21 00:08:39 +0000850 default_priority = job_fields['priority'].default
jadmanski0afbb632008-06-06 21:10:57 +0000851 default_string = models.Job.Priority.get_string(default_priority)
852 result['default_priority'] = default_string
853 result['users'] = get_users(sort_by=['login'])
854 result['labels'] = get_labels(sort_by=['-platform', 'name'])
showardc92da832009-04-07 18:14:34 +0000855 result['atomic_groups'] = get_atomic_groups(sort_by=['name'])
jadmanski0afbb632008-06-06 21:10:57 +0000856 result['tests'] = get_tests(sort_by=['name'])
showard2b9a88b2008-06-13 20:55:03 +0000857 result['profilers'] = get_profilers(sort_by=['name'])
showard0fc38302008-10-23 00:44:07 +0000858 result['current_user'] = rpc_utils.prepare_for_serialization(
showard64a95952010-01-13 21:27:16 +0000859 models.User.current_user().get_object_dict())
showard2b9a88b2008-06-13 20:55:03 +0000860 result['host_statuses'] = sorted(models.Host.Status.names)
mbligh5a198b92008-12-11 19:33:29 +0000861 result['job_statuses'] = sorted(models.HostQueueEntry.Status.names)
showardb1e51872008-10-07 11:08:18 +0000862 result['job_timeout_default'] = models.Job.DEFAULT_TIMEOUT
Scott Zawalskia8a0d752012-11-08 14:13:43 -0800863 result['job_max_runtime_hrs_default'] = models.Job.DEFAULT_MAX_RUNTIME_HRS
showarda1e74b32009-05-12 17:32:04 +0000864 result['parse_failed_repair_default'] = bool(
865 models.Job.DEFAULT_PARSE_FAILED_REPAIR)
jamesrendd855242010-03-02 22:23:44 +0000866 result['reboot_before_options'] = model_attributes.RebootBefore.names
867 result['reboot_after_options'] = model_attributes.RebootAfter.names
showard8fbae652009-01-20 23:23:10 +0000868 result['motd'] = rpc_utils.get_motd()
jamesren76fcf192010-04-21 20:39:50 +0000869 result['drone_sets_enabled'] = models.DroneSet.drone_sets_enabled()
870 result['drone_sets'] = drone_sets
jamesren4a41e012010-07-16 22:33:48 +0000871 result['parameterized_jobs'] = models.Job.parameterized_jobs_enabled()
showard8ac29b42008-07-17 17:01:55 +0000872
showardd3dc1992009-04-22 21:01:40 +0000873 result['status_dictionary'] = {"Aborted": "Aborted",
showard8ac29b42008-07-17 17:01:55 +0000874 "Verifying": "Verifying Host",
875 "Pending": "Waiting on other hosts",
876 "Running": "Running autoserv",
877 "Completed": "Autoserv completed",
878 "Failed": "Failed to complete",
showardd823b362008-07-24 16:35:46 +0000879 "Queued": "Queued",
showard5deb6772008-11-04 21:54:33 +0000880 "Starting": "Next in host's queue",
881 "Stopped": "Other host(s) failed verify",
showardd3dc1992009-04-22 21:01:40 +0000882 "Parsing": "Awaiting parse of final results",
showard29f7cd22009-04-29 21:16:24 +0000883 "Gathering": "Gathering log files",
showard8cc058f2009-09-08 16:26:33 +0000884 "Template": "Template job for recurring run",
mbligh4608b002010-01-05 18:22:35 +0000885 "Waiting": "Waiting for scheduler action",
886 "Archiving": "Archiving results"}
jadmanski0afbb632008-06-06 21:10:57 +0000887 return result
showard29f7cd22009-04-29 21:16:24 +0000888
889
890def get_server_time():
891 return datetime.datetime.now().strftime("%Y-%m-%d %H:%M")