blob: 0c7130aef899a0d560b239cc34780f0bf23010d2 [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
Simran Basib6ec8ae2014-04-23 12:05:08 -070036from autotest_lib.client.common_lib import priorities
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
Simran Basib6ec8ae2014-04-23 12:05:08 -070039from autotest_lib.frontend.afe import site_rpc_interface
Jiaxi Luoaac54572014-06-04 13:57:02 -070040from autotest_lib.frontend.tko import rpc_interface as tko_rpc_interface
Simran Basi71206ef2014-08-13 13:51:18 -070041from autotest_lib.server import utils
Jiaxi Luo90190c92014-06-18 12:35:57 -070042from autotest_lib.server.cros.dynamic_suite import tools
mblighe8819cd2008-02-15 16:48:40 +000043
Eric Lid23bc192011-02-09 14:38:57 -080044def get_parameterized_autoupdate_image_url(job):
45 """Get the parameterized autoupdate image url from a parameterized job."""
46 known_test_obj = models.Test.smart_get('autoupdate_ParameterizedJob')
47 image_parameter = known_test_obj.testparameter_set.get(test=known_test_obj,
beeps8bb1f7d2013-08-05 01:30:09 -070048 name='image')
Eric Lid23bc192011-02-09 14:38:57 -080049 para_set = job.parameterized_job.parameterizedjobparameter_set
50 job_test_para = para_set.get(test_parameter=image_parameter)
51 return job_test_para.parameter_value
52
53
mblighe8819cd2008-02-15 16:48:40 +000054# labels
55
showard989f25d2008-10-01 11:38:11 +000056def add_label(name, kernel_config=None, platform=None, only_if_needed=None):
showardc92da832009-04-07 18:14:34 +000057 return models.Label.add_object(
58 name=name, kernel_config=kernel_config, platform=platform,
59 only_if_needed=only_if_needed).id
mblighe8819cd2008-02-15 16:48:40 +000060
61
62def modify_label(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +000063 models.Label.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +000064
65
66def delete_label(id):
jadmanski0afbb632008-06-06 21:10:57 +000067 models.Label.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +000068
69
showardbbabf502008-06-06 00:02:02 +000070def label_add_hosts(id, hosts):
showardbe3ec042008-11-12 18:16:07 +000071 host_objs = models.Host.smart_get_bulk(hosts)
showardcafd16e2009-05-29 18:37:49 +000072 label = models.Label.smart_get(id)
73 if label.platform:
74 models.Host.check_no_platform(host_objs)
75 label.host_set.add(*host_objs)
showardbbabf502008-06-06 00:02:02 +000076
77
78def label_remove_hosts(id, hosts):
showardbe3ec042008-11-12 18:16:07 +000079 host_objs = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +000080 models.Label.smart_get(id).host_set.remove(*host_objs)
showardbbabf502008-06-06 00:02:02 +000081
82
Jiaxi Luo31874592014-06-11 10:36:35 -070083def get_labels(exclude_filters=(), **filter_data):
showardc92da832009-04-07 18:14:34 +000084 """\
Jiaxi Luo31874592014-06-11 10:36:35 -070085 @param exclude_filters: A sequence of dictionaries of filters.
86
showardc92da832009-04-07 18:14:34 +000087 @returns A sequence of nested dictionaries of label information.
88 """
Jiaxi Luo31874592014-06-11 10:36:35 -070089 labels = models.Label.query_objects(filter_data)
90 for exclude_filter in exclude_filters:
91 labels = labels.exclude(**exclude_filter)
92 return rpc_utils.prepare_rows_as_nested_dicts(labels, ('atomic_group',))
showardc92da832009-04-07 18:14:34 +000093
94
95# atomic groups
96
showarde9450c92009-06-30 01:58:52 +000097def add_atomic_group(name, max_number_of_machines=None, description=None):
showardc92da832009-04-07 18:14:34 +000098 return models.AtomicGroup.add_object(
99 name=name, max_number_of_machines=max_number_of_machines,
100 description=description).id
101
102
103def modify_atomic_group(id, **data):
104 models.AtomicGroup.smart_get(id).update_object(data)
105
106
107def delete_atomic_group(id):
108 models.AtomicGroup.smart_get(id).delete()
109
110
111def atomic_group_add_labels(id, labels):
112 label_objs = models.Label.smart_get_bulk(labels)
113 models.AtomicGroup.smart_get(id).label_set.add(*label_objs)
114
115
116def atomic_group_remove_labels(id, labels):
117 label_objs = models.Label.smart_get_bulk(labels)
118 models.AtomicGroup.smart_get(id).label_set.remove(*label_objs)
119
120
121def get_atomic_groups(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000122 return rpc_utils.prepare_for_serialization(
showardc92da832009-04-07 18:14:34 +0000123 models.AtomicGroup.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000124
125
126# hosts
127
showarddf062562008-07-03 19:56:37 +0000128def add_host(hostname, status=None, locked=None, protection=None):
jadmanski0afbb632008-06-06 21:10:57 +0000129 return models.Host.add_object(hostname=hostname, status=status,
showarddf062562008-07-03 19:56:37 +0000130 locked=locked, protection=protection).id
mblighe8819cd2008-02-15 16:48:40 +0000131
132
Jakob Juelich50e91f72014-10-01 12:43:23 -0700133@rpc_utils.forward_single_host_rpc_to_shard
mblighe8819cd2008-02-15 16:48:40 +0000134def modify_host(id, **data):
Jakob Juelich50e91f72014-10-01 12:43:23 -0700135 """Modify local attributes of a host.
136
137 If this is called on the master, but the host is assigned to a shard, this
138 will also forward the call to the responsible shard. This means i.e. if a
139 host is being locked using this function, this change will also propagate to
140 shards.
141
142 @param id: id of the host to modify.
143 @param **data: key=value pairs of values to set on the host.
144 """
showardbe0d8692009-08-20 23:42:44 +0000145 rpc_utils.check_modify_host(data)
showardce7c0922009-09-11 18:39:24 +0000146 host = models.Host.smart_get(id)
Jakob Juelich50e91f72014-10-01 12:43:23 -0700147
showardce7c0922009-09-11 18:39:24 +0000148 rpc_utils.check_modify_host_locking(host, data)
149 host.update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000150
151
showard276f9442009-05-20 00:33:16 +0000152def modify_hosts(host_filter_data, update_data):
Jakob Juelich50e91f72014-10-01 12:43:23 -0700153 """Modify local attributes of multiple hosts.
154
155 If this is called on the master, but one of the hosts in that match the
156 filters is assigned to a shard, this will also forward the call to the
157 responsible shard.
158
159 The filters are always applied on the master, not on the shards. This means
160 if the states of a host differ on the master and a shard, the state on the
161 master will be used. I.e. this means:
162 A host was synced to Shard 1. On Shard 1 the status of the host was set to
163 'Repair Failed'.
164 - A call to modify_hosts with host_filter_data={'status': 'Ready'} will
165 update the host (both on the shard and on the master), because the state
166 of the host as the master knows it is still 'Ready'.
167 - A call to modify_hosts with host_filter_data={'status': 'Repair failed'
168 will not update the host, because the filter doesn't apply on the master.
169
showardbe0d8692009-08-20 23:42:44 +0000170 @param host_filter_data: Filters out which hosts to modify.
171 @param update_data: A dictionary with the changes to make to the hosts.
showard276f9442009-05-20 00:33:16 +0000172 """
showardbe0d8692009-08-20 23:42:44 +0000173 rpc_utils.check_modify_host(update_data)
showard276f9442009-05-20 00:33:16 +0000174 hosts = models.Host.query_objects(host_filter_data)
Jakob Juelich50e91f72014-10-01 12:43:23 -0700175
176 affected_shard_hostnames = set()
177 affected_host_ids = []
178
Alex Miller9658a952013-05-14 16:40:02 -0700179 # Check all hosts before changing data for exception safety.
180 for host in hosts:
181 rpc_utils.check_modify_host_locking(host, update_data)
Jakob Juelich50e91f72014-10-01 12:43:23 -0700182 if host.shard:
183 affected_shard_hostnames.add(host.shard.hostname)
184 affected_host_ids.append(host.id)
185
186 if not rpc_utils.is_shard():
187 # Caution: Changing the filter from the original here. See docstring.
188 rpc_utils.run_rpc_on_multiple_hostnames(
189 'modify_hosts', affected_shard_hostnames,
190 host_filter_data={'id__in': affected_host_ids},
191 update_data=update_data)
192
showard276f9442009-05-20 00:33:16 +0000193 for host in hosts:
194 host.update_object(update_data)
195
196
mblighe8819cd2008-02-15 16:48:40 +0000197def host_add_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000198 labels = models.Label.smart_get_bulk(labels)
showardcafd16e2009-05-29 18:37:49 +0000199 host = models.Host.smart_get(id)
200
201 platforms = [label.name for label in labels if label.platform]
202 if len(platforms) > 1:
203 raise model_logic.ValidationError(
204 {'labels': 'Adding more than one platform label: %s' %
205 ', '.join(platforms)})
206 if len(platforms) == 1:
207 models.Host.check_no_platform([host])
208 host.labels.add(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000209
210
211def host_remove_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000212 labels = models.Label.smart_get_bulk(labels)
jadmanski0afbb632008-06-06 21:10:57 +0000213 models.Host.smart_get(id).labels.remove(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000214
215
MK Ryuacf35922014-10-03 14:56:49 -0700216def get_host_attribute(attribute, **host_filter_data):
217 """
218 @param attribute: string name of attribute
219 @param host_filter_data: filter data to apply to Hosts to choose hosts to
220 act upon
221 """
222 hosts = rpc_utils.get_host_query((), False, False, True, host_filter_data)
223 hosts = list(hosts)
224 models.Host.objects.populate_relationships(hosts, models.HostAttribute,
225 'attribute_list')
226 host_attr_dicts = []
227 for host_obj in hosts:
228 for attr_obj in host_obj.attribute_list:
229 if attr_obj.attribute == attribute:
230 host_attr_dicts.append(attr_obj.get_object_dict())
231 return rpc_utils.prepare_for_serialization(host_attr_dicts)
232
233
showard0957a842009-05-11 19:25:08 +0000234def set_host_attribute(attribute, value, **host_filter_data):
235 """
236 @param attribute string name of attribute
237 @param value string, or None to delete an attribute
238 @param host_filter_data filter data to apply to Hosts to choose hosts to act
239 upon
240 """
241 assert host_filter_data # disallow accidental actions on all hosts
242 hosts = models.Host.query_objects(host_filter_data)
243 models.AclGroup.check_for_acl_violation_hosts(hosts)
244
245 for host in hosts:
showardf8b19042009-05-12 17:22:49 +0000246 host.set_or_delete_attribute(attribute, value)
showard0957a842009-05-11 19:25:08 +0000247
248
Jakob Juelich50e91f72014-10-01 12:43:23 -0700249@rpc_utils.forward_single_host_rpc_to_shard
mblighe8819cd2008-02-15 16:48:40 +0000250def delete_host(id):
jadmanski0afbb632008-06-06 21:10:57 +0000251 models.Host.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000252
253
showard87cc38f2009-08-20 23:37:04 +0000254def get_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000255 exclude_atomic_group_hosts=False, valid_only=True, **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000256 """
257 @param multiple_labels: match hosts in all of the labels given. Should
258 be a list of label names.
259 @param exclude_only_if_needed_labels: Exclude hosts with at least one
260 "only_if_needed" label applied.
261 @param exclude_atomic_group_hosts: Exclude hosts that have one or more
262 atomic group labels associated with them.
jadmanski0afbb632008-06-06 21:10:57 +0000263 """
showard43a3d262008-11-12 18:17:05 +0000264 hosts = rpc_utils.get_host_query(multiple_labels,
265 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000266 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000267 valid_only, filter_data)
showard0957a842009-05-11 19:25:08 +0000268 hosts = list(hosts)
269 models.Host.objects.populate_relationships(hosts, models.Label,
270 'label_list')
271 models.Host.objects.populate_relationships(hosts, models.AclGroup,
272 'acl_list')
273 models.Host.objects.populate_relationships(hosts, models.HostAttribute,
274 'attribute_list')
showard43a3d262008-11-12 18:17:05 +0000275 host_dicts = []
276 for host_obj in hosts:
277 host_dict = host_obj.get_object_dict()
showard0957a842009-05-11 19:25:08 +0000278 host_dict['labels'] = [label.name for label in host_obj.label_list]
showard909c9142009-07-07 20:54:42 +0000279 host_dict['platform'], host_dict['atomic_group'] = (rpc_utils.
280 find_platform_and_atomic_group(host_obj))
showard0957a842009-05-11 19:25:08 +0000281 host_dict['acls'] = [acl.name for acl in host_obj.acl_list]
282 host_dict['attributes'] = dict((attribute.attribute, attribute.value)
283 for attribute in host_obj.attribute_list)
showard43a3d262008-11-12 18:17:05 +0000284 host_dicts.append(host_dict)
285 return rpc_utils.prepare_for_serialization(host_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000286
287
showard87cc38f2009-08-20 23:37:04 +0000288def get_num_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000289 exclude_atomic_group_hosts=False, valid_only=True,
290 **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000291 """
292 Same parameters as get_hosts().
293
294 @returns The number of matching hosts.
295 """
showard43a3d262008-11-12 18:17:05 +0000296 hosts = rpc_utils.get_host_query(multiple_labels,
297 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000298 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000299 valid_only, filter_data)
showard43a3d262008-11-12 18:17:05 +0000300 return hosts.count()
showard1385b162008-03-13 15:59:40 +0000301
mblighe8819cd2008-02-15 16:48:40 +0000302
303# tests
304
showard909c7a62008-07-15 21:52:38 +0000305def add_test(name, test_type, path, author=None, dependencies=None,
showard3d9899a2008-07-31 02:11:58 +0000306 experimental=True, run_verify=None, test_class=None,
showard909c7a62008-07-15 21:52:38 +0000307 test_time=None, test_category=None, description=None,
308 sync_count=1):
jadmanski0afbb632008-06-06 21:10:57 +0000309 return models.Test.add_object(name=name, test_type=test_type, path=path,
showard909c7a62008-07-15 21:52:38 +0000310 author=author, dependencies=dependencies,
311 experimental=experimental,
312 run_verify=run_verify, test_time=test_time,
313 test_category=test_category,
314 sync_count=sync_count,
jadmanski0afbb632008-06-06 21:10:57 +0000315 test_class=test_class,
316 description=description).id
mblighe8819cd2008-02-15 16:48:40 +0000317
318
319def modify_test(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000320 models.Test.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000321
322
323def delete_test(id):
jadmanski0afbb632008-06-06 21:10:57 +0000324 models.Test.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000325
326
327def get_tests(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000328 return rpc_utils.prepare_for_serialization(
329 models.Test.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000330
331
showard2b9a88b2008-06-13 20:55:03 +0000332# profilers
333
334def add_profiler(name, description=None):
335 return models.Profiler.add_object(name=name, description=description).id
336
337
338def modify_profiler(id, **data):
339 models.Profiler.smart_get(id).update_object(data)
340
341
342def delete_profiler(id):
343 models.Profiler.smart_get(id).delete()
344
345
346def get_profilers(**filter_data):
347 return rpc_utils.prepare_for_serialization(
348 models.Profiler.list_objects(filter_data))
349
350
mblighe8819cd2008-02-15 16:48:40 +0000351# users
352
353def add_user(login, access_level=None):
jadmanski0afbb632008-06-06 21:10:57 +0000354 return models.User.add_object(login=login, access_level=access_level).id
mblighe8819cd2008-02-15 16:48:40 +0000355
356
357def modify_user(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000358 models.User.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000359
360
361def delete_user(id):
jadmanski0afbb632008-06-06 21:10:57 +0000362 models.User.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000363
364
365def get_users(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000366 return rpc_utils.prepare_for_serialization(
367 models.User.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000368
369
370# acl groups
371
372def add_acl_group(name, description=None):
showard04f2cd82008-07-25 20:53:31 +0000373 group = models.AclGroup.add_object(name=name, description=description)
showard64a95952010-01-13 21:27:16 +0000374 group.users.add(models.User.current_user())
showard04f2cd82008-07-25 20:53:31 +0000375 return group.id
mblighe8819cd2008-02-15 16:48:40 +0000376
377
378def modify_acl_group(id, **data):
showard04f2cd82008-07-25 20:53:31 +0000379 group = models.AclGroup.smart_get(id)
380 group.check_for_acl_violation_acl_group()
381 group.update_object(data)
382 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000383
384
385def acl_group_add_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000386 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000387 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000388 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000389 group.users.add(*users)
mblighe8819cd2008-02-15 16:48:40 +0000390
391
392def acl_group_remove_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000393 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000394 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000395 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000396 group.users.remove(*users)
showard04f2cd82008-07-25 20:53:31 +0000397 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000398
399
400def acl_group_add_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000401 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000402 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000403 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000404 group.hosts.add(*hosts)
showard08f981b2008-06-24 21:59:03 +0000405 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000406
407
408def acl_group_remove_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000409 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000410 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000411 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000412 group.hosts.remove(*hosts)
showard08f981b2008-06-24 21:59:03 +0000413 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000414
415
416def delete_acl_group(id):
jadmanski0afbb632008-06-06 21:10:57 +0000417 models.AclGroup.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000418
419
420def get_acl_groups(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000421 acl_groups = models.AclGroup.list_objects(filter_data)
422 for acl_group in acl_groups:
423 acl_group_obj = models.AclGroup.objects.get(id=acl_group['id'])
424 acl_group['users'] = [user.login
425 for user in acl_group_obj.users.all()]
426 acl_group['hosts'] = [host.hostname
427 for host in acl_group_obj.hosts.all()]
428 return rpc_utils.prepare_for_serialization(acl_groups)
mblighe8819cd2008-02-15 16:48:40 +0000429
430
431# jobs
432
mbligh120351e2009-01-24 01:40:45 +0000433def generate_control_file(tests=(), kernel=None, label=None, profilers=(),
showard91f85102009-10-12 20:34:52 +0000434 client_control_file='', use_container=False,
showard232b7ae2009-11-10 00:46:48 +0000435 profile_only=None, upload_kernel_config=False):
jadmanski0afbb632008-06-06 21:10:57 +0000436 """
mbligh120351e2009-01-24 01:40:45 +0000437 Generates a client-side control file to load a kernel and run tests.
438
439 @param tests List of tests to run.
mbligha3c58d22009-08-24 22:01:51 +0000440 @param kernel A list of kernel info dictionaries configuring which kernels
441 to boot for this job and other options for them
mbligh120351e2009-01-24 01:40:45 +0000442 @param label Name of label to grab kernel config from.
443 @param profilers List of profilers to activate during the job.
444 @param client_control_file The contents of a client-side control file to
445 run at the end of all tests. If this is supplied, all tests must be
446 client side.
447 TODO: in the future we should support server control files directly
448 to wrap with a kernel. That'll require changing the parameter
449 name and adding a boolean to indicate if it is a client or server
450 control file.
451 @param use_container unused argument today. TODO: Enable containers
452 on the host during a client side test.
showard91f85102009-10-12 20:34:52 +0000453 @param profile_only A boolean that indicates what default profile_only
454 mode to use in the control file. Passing None will generate a
455 control file that does not explcitly set the default mode at all.
showard232b7ae2009-11-10 00:46:48 +0000456 @param upload_kernel_config: if enabled it will generate server control
457 file code that uploads the kernel config file to the client and
458 tells the client of the new (local) path when compiling the kernel;
459 the tests must be server side tests
mbligh120351e2009-01-24 01:40:45 +0000460
461 @returns a dict with the following keys:
462 control_file: str, The control file text.
463 is_server: bool, is the control file a server-side control file?
464 synch_count: How many machines the job uses per autoserv execution.
465 synch_count == 1 means the job is asynchronous.
466 dependencies: A list of the names of labels on which the job depends.
467 """
showardd86debe2009-06-10 17:37:56 +0000468 if not tests and not client_control_file:
showard2bab8f42008-11-12 18:15:22 +0000469 return dict(control_file='', is_server=False, synch_count=1,
showard989f25d2008-10-01 11:38:11 +0000470 dependencies=[])
mblighe8819cd2008-02-15 16:48:40 +0000471
showard989f25d2008-10-01 11:38:11 +0000472 cf_info, test_objects, profiler_objects, label = (
showard2b9a88b2008-06-13 20:55:03 +0000473 rpc_utils.prepare_generate_control_file(tests, kernel, label,
474 profilers))
showard989f25d2008-10-01 11:38:11 +0000475 cf_info['control_file'] = control_file.generate_control(
mbligha3c58d22009-08-24 22:01:51 +0000476 tests=test_objects, kernels=kernel, platform=label,
mbligh120351e2009-01-24 01:40:45 +0000477 profilers=profiler_objects, is_server=cf_info['is_server'],
showard232b7ae2009-11-10 00:46:48 +0000478 client_control_file=client_control_file, profile_only=profile_only,
479 upload_kernel_config=upload_kernel_config)
showard989f25d2008-10-01 11:38:11 +0000480 return cf_info
mblighe8819cd2008-02-15 16:48:40 +0000481
482
jamesren4a41e012010-07-16 22:33:48 +0000483def create_parameterized_job(name, priority, test, parameters, kernel=None,
484 label=None, profilers=(), profiler_parameters=None,
485 use_container=False, profile_only=None,
486 upload_kernel_config=False, hosts=(),
487 meta_hosts=(), one_time_hosts=(),
488 atomic_group_name=None, synch_count=None,
489 is_template=False, timeout=None,
Simran Basi7e605742013-11-12 13:43:36 -0800490 timeout_mins=None, max_runtime_mins=None,
491 run_verify=False, email_list='', dependencies=(),
492 reboot_before=None, reboot_after=None,
493 parse_failed_repair=None, hostless=False,
494 keyvals=None, drone_set=None, run_reset=True):
jamesren4a41e012010-07-16 22:33:48 +0000495 """
496 Creates and enqueues a parameterized job.
497
498 Most parameters a combination of the parameters for generate_control_file()
499 and create_job(), with the exception of:
500
501 @param test name or ID of the test to run
502 @param parameters a map of parameter name ->
503 tuple of (param value, param type)
504 @param profiler_parameters a dictionary of parameters for the profilers:
505 key: profiler name
506 value: dict of param name -> tuple of
507 (param value,
508 param type)
509 """
510 # Save the values of the passed arguments here. What we're going to do with
511 # them is pass them all to rpc_utils.get_create_job_common_args(), which
512 # will extract the subset of these arguments that apply for
513 # rpc_utils.create_job_common(), which we then pass in to that function.
514 args = locals()
515
516 # Set up the parameterized job configs
517 test_obj = models.Test.smart_get(test)
Aviv Keshet3dd8beb2013-05-13 17:36:04 -0700518 control_type = test_obj.test_type
jamesren4a41e012010-07-16 22:33:48 +0000519
520 try:
521 label = models.Label.smart_get(label)
522 except models.Label.DoesNotExist:
523 label = None
524
525 kernel_objs = models.Kernel.create_kernels(kernel)
526 profiler_objs = [models.Profiler.smart_get(profiler)
527 for profiler in profilers]
528
529 parameterized_job = models.ParameterizedJob.objects.create(
530 test=test_obj, label=label, use_container=use_container,
531 profile_only=profile_only,
532 upload_kernel_config=upload_kernel_config)
533 parameterized_job.kernels.add(*kernel_objs)
534
535 for profiler in profiler_objs:
536 parameterized_profiler = models.ParameterizedJobProfiler.objects.create(
537 parameterized_job=parameterized_job,
538 profiler=profiler)
539 profiler_params = profiler_parameters.get(profiler.name, {})
540 for name, (value, param_type) in profiler_params.iteritems():
541 models.ParameterizedJobProfilerParameter.objects.create(
542 parameterized_job_profiler=parameterized_profiler,
543 parameter_name=name,
544 parameter_value=value,
545 parameter_type=param_type)
546
547 try:
548 for parameter in test_obj.testparameter_set.all():
549 if parameter.name in parameters:
550 param_value, param_type = parameters.pop(parameter.name)
551 parameterized_job.parameterizedjobparameter_set.create(
552 test_parameter=parameter, parameter_value=param_value,
553 parameter_type=param_type)
554
555 if parameters:
556 raise Exception('Extra parameters remain: %r' % parameters)
557
558 return rpc_utils.create_job_common(
559 parameterized_job=parameterized_job.id,
560 control_type=control_type,
561 **rpc_utils.get_create_job_common_args(args))
562 except:
563 parameterized_job.delete()
564 raise
565
566
Simran Basib6ec8ae2014-04-23 12:05:08 -0700567def create_job_page_handler(name, priority, control_file, control_type,
568 image=None, hostless=False, **kwargs):
569 """\
570 Create and enqueue a job.
571
572 @param name name of this job
573 @param priority Integer priority of this job. Higher is more important.
574 @param control_file String contents of the control file.
575 @param control_type Type of control file, Client or Server.
576 @param kwargs extra args that will be required by create_suite_job or
577 create_job.
578
579 @returns The created Job id number.
580 """
581 control_file = rpc_utils.encode_ascii(control_file)
Jiaxi Luodd67beb2014-07-18 16:28:31 -0700582 if not control_file:
583 raise model_logic.ValidationError({
584 'control_file' : "Control file cannot be empty"})
Simran Basib6ec8ae2014-04-23 12:05:08 -0700585
586 if image and hostless:
587 return site_rpc_interface.create_suite_job(
588 name=name, control_file=control_file, priority=priority,
589 build=image, **kwargs)
590 return create_job(name, priority, control_file, control_type, image=image,
591 hostless=hostless, **kwargs)
592
593
showard12f3e322009-05-13 21:27:42 +0000594def create_job(name, priority, control_file, control_type,
595 hosts=(), meta_hosts=(), one_time_hosts=(),
596 atomic_group_name=None, synch_count=None, is_template=False,
Simran Basi7e605742013-11-12 13:43:36 -0800597 timeout=None, timeout_mins=None, max_runtime_mins=None,
598 run_verify=False, email_list='', dependencies=(),
599 reboot_before=None, reboot_after=None, parse_failed_repair=None,
600 hostless=False, keyvals=None, drone_set=None, image=None,
Jiaxi Luo90190c92014-06-18 12:35:57 -0700601 parent_job_id=None, test_retry=0, run_reset=True, args=(),
602 **kwargs):
jadmanski0afbb632008-06-06 21:10:57 +0000603 """\
604 Create and enqueue a job.
mblighe8819cd2008-02-15 16:48:40 +0000605
showarda1e74b32009-05-12 17:32:04 +0000606 @param name name of this job
Alex Miller7d658cf2013-09-04 16:00:35 -0700607 @param priority Integer priority of this job. Higher is more important.
showarda1e74b32009-05-12 17:32:04 +0000608 @param control_file String contents of the control file.
609 @param control_type Type of control file, Client or Server.
610 @param synch_count How many machines the job uses per autoserv execution.
Jiaxi Luo90190c92014-06-18 12:35:57 -0700611 synch_count == 1 means the job is asynchronous. If an atomic group is
612 given this value is treated as a minimum.
showarda1e74b32009-05-12 17:32:04 +0000613 @param is_template If true then create a template job.
614 @param timeout Hours after this call returns until the job times out.
Simran Basi7e605742013-11-12 13:43:36 -0800615 @param timeout_mins Minutes after this call returns until the job times
Jiaxi Luo90190c92014-06-18 12:35:57 -0700616 out.
Simran Basi34217022012-11-06 13:43:15 -0800617 @param max_runtime_mins Minutes from job starting time until job times out
showarda1e74b32009-05-12 17:32:04 +0000618 @param run_verify Should the host be verified before running the test?
619 @param email_list String containing emails to mail when the job is done
620 @param dependencies List of label names on which this job depends
621 @param reboot_before Never, If dirty, or Always
622 @param reboot_after Never, If all tests passed, or Always
623 @param parse_failed_repair if true, results of failed repairs launched by
Jiaxi Luo90190c92014-06-18 12:35:57 -0700624 this job will be parsed as part of the job.
showarda9545c02009-12-18 22:44:26 +0000625 @param hostless if true, create a hostless job
showardc1a98d12010-01-15 00:22:22 +0000626 @param keyvals dict of keyvals to associate with the job
showarda1e74b32009-05-12 17:32:04 +0000627 @param hosts List of hosts to run job on.
628 @param meta_hosts List where each entry is a label name, and for each entry
Jiaxi Luo90190c92014-06-18 12:35:57 -0700629 one host will be chosen from that label to run the job on.
showarda1e74b32009-05-12 17:32:04 +0000630 @param one_time_hosts List of hosts not in the database to run the job on.
631 @param atomic_group_name The name of an atomic group to schedule the job on.
jamesren76fcf192010-04-21 20:39:50 +0000632 @param drone_set The name of the drone set to run this test on.
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800633 @param image OS image to install before running job.
Aviv Keshet0b9cfc92013-02-05 11:36:02 -0800634 @param parent_job_id id of a job considered to be parent of created job.
Simran Basib6ec8ae2014-04-23 12:05:08 -0700635 @param test_retry Number of times to retry test if the test did not
Jiaxi Luo90190c92014-06-18 12:35:57 -0700636 complete successfully. (optional, default: 0)
Simran Basib6ec8ae2014-04-23 12:05:08 -0700637 @param run_reset Should the host be reset before running the test?
Jiaxi Luo90190c92014-06-18 12:35:57 -0700638 @param args A list of args to be injected into control file.
Simran Basib6ec8ae2014-04-23 12:05:08 -0700639 @param kwargs extra keyword args. NOT USED.
showardc92da832009-04-07 18:14:34 +0000640
641 @returns The created Job id number.
jadmanski0afbb632008-06-06 21:10:57 +0000642 """
Jiaxi Luo90190c92014-06-18 12:35:57 -0700643 if args:
644 control_file = tools.inject_vars({'args': args}, control_file)
645
Simran Basiab5a1bf2014-05-28 15:39:44 -0700646 if image is None:
647 return rpc_utils.create_job_common(
648 **rpc_utils.get_create_job_common_args(locals()))
649
650 # When image is supplied use a known parameterized test already in the
651 # database to pass the OS image path from the front end, through the
652 # scheduler, and finally to autoserv as the --image parameter.
653
654 # The test autoupdate_ParameterizedJob is in afe_autotests and used to
655 # instantiate a Test object and from there a ParameterizedJob.
656 known_test_obj = models.Test.smart_get('autoupdate_ParameterizedJob')
657 known_parameterized_job = models.ParameterizedJob.objects.create(
658 test=known_test_obj)
659
660 # autoupdate_ParameterizedJob has a single parameter, the image parameter,
661 # stored in the table afe_test_parameters. We retrieve and set this
662 # instance of the parameter to the OS image path.
663 image_parameter = known_test_obj.testparameter_set.get(test=known_test_obj,
664 name='image')
665 known_parameterized_job.parameterizedjobparameter_set.create(
666 test_parameter=image_parameter, parameter_value=image,
667 parameter_type='string')
668
669 # By passing a parameterized_job to create_job_common the job entry in
670 # the afe_jobs table will have the field parameterized_job_id set.
671 # The scheduler uses this id in the afe_parameterized_jobs table to
672 # match this job to our known test, and then with the
673 # afe_parameterized_job_parameters table to get the actual image path.
jamesren4a41e012010-07-16 22:33:48 +0000674 return rpc_utils.create_job_common(
Simran Basiab5a1bf2014-05-28 15:39:44 -0700675 parameterized_job=known_parameterized_job.id,
jamesren4a41e012010-07-16 22:33:48 +0000676 **rpc_utils.get_create_job_common_args(locals()))
mblighe8819cd2008-02-15 16:48:40 +0000677
678
showard9dbdcda2008-10-14 17:34:36 +0000679def abort_host_queue_entries(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000680 """\
showard9dbdcda2008-10-14 17:34:36 +0000681 Abort a set of host queue entries.
jadmanski0afbb632008-06-06 21:10:57 +0000682 """
showard9dbdcda2008-10-14 17:34:36 +0000683 query = models.HostQueueEntry.query_objects(filter_data)
beepsfaecbce2013-10-29 11:35:10 -0700684
685 # Dont allow aborts on:
686 # 1. Jobs that have already completed (whether or not they were aborted)
687 # 2. Jobs that we have already been aborted (but may not have completed)
688 query = query.filter(complete=False).filter(aborted=False)
showarddc817512008-11-12 18:16:41 +0000689 models.AclGroup.check_abort_permissions(query)
showard9dbdcda2008-10-14 17:34:36 +0000690 host_queue_entries = list(query.select_related())
showard2bab8f42008-11-12 18:15:22 +0000691 rpc_utils.check_abort_synchronous_jobs(host_queue_entries)
mblighe8819cd2008-02-15 16:48:40 +0000692
Simran Basic1b26762013-06-26 14:23:21 -0700693 models.HostQueueEntry.abort_host_queue_entries(host_queue_entries)
showard9d821ab2008-07-11 16:54:29 +0000694
695
beeps8bb1f7d2013-08-05 01:30:09 -0700696def abort_special_tasks(**filter_data):
697 """\
698 Abort the special task, or tasks, specified in the filter.
699 """
700 query = models.SpecialTask.query_objects(filter_data)
701 special_tasks = query.filter(is_active=True)
702 for task in special_tasks:
703 task.abort()
704
705
Simran Basi73dae552013-02-25 14:57:46 -0800706def _call_special_tasks_on_hosts(task, hosts):
707 """\
708 Schedules a set of hosts for a special task.
709
710 @returns A list of hostnames that a special task was created for.
711 """
712 models.AclGroup.check_for_acl_violation_hosts(hosts)
713 for host in hosts:
714 models.SpecialTask.schedule_special_task(host, task)
715 return list(sorted(host.hostname for host in hosts))
716
717
showard1ff7b2e2009-05-15 23:17:18 +0000718def reverify_hosts(**filter_data):
719 """\
720 Schedules a set of hosts for verify.
mbligh4e545a52009-12-19 05:30:39 +0000721
722 @returns A list of hostnames that a verify task was created for.
showard1ff7b2e2009-05-15 23:17:18 +0000723 """
Simran Basi73dae552013-02-25 14:57:46 -0800724 return _call_special_tasks_on_hosts(models.SpecialTask.Task.VERIFY,
725 models.Host.query_objects(filter_data))
726
727
728def repair_hosts(**filter_data):
729 """\
730 Schedules a set of hosts for repair.
731
732 @returns A list of hostnames that a repair task was created for.
733 """
734 return _call_special_tasks_on_hosts(models.SpecialTask.Task.REPAIR,
735 models.Host.query_objects(filter_data))
showard1ff7b2e2009-05-15 23:17:18 +0000736
737
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700738def get_jobs(not_yet_run=False, running=False, finished=False,
739 suite=False, sub=False, standalone=False, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000740 """\
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700741 Extra status filter args for get_jobs:
jadmanski0afbb632008-06-06 21:10:57 +0000742 -not_yet_run: Include only jobs that have not yet started running.
743 -running: Include only jobs that have start running but for which not
744 all hosts have completed.
745 -finished: Include only jobs for which all hosts have completed (or
746 aborted).
747 At most one of these three fields should be specified.
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700748
749 Extra type filter args for get_jobs:
750 -suite: Include only jobs with child jobs.
751 -sub: Include only jobs with a parent job.
752 -standalone: Inlcude only jobs with no child or parent jobs.
753 At most one of these three fields should be specified.
jadmanski0afbb632008-06-06 21:10:57 +0000754 """
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700755 extra_args = rpc_utils.extra_job_status_filters(not_yet_run,
756 running,
757 finished)
758 filter_data['extra_args'] = rpc_utils.extra_job_type_filters(extra_args,
759 suite,
760 sub,
761 standalone)
showard0957a842009-05-11 19:25:08 +0000762 job_dicts = []
763 jobs = list(models.Job.query_objects(filter_data))
764 models.Job.objects.populate_relationships(jobs, models.Label,
765 'dependencies')
showardc1a98d12010-01-15 00:22:22 +0000766 models.Job.objects.populate_relationships(jobs, models.JobKeyval, 'keyvals')
showard0957a842009-05-11 19:25:08 +0000767 for job in jobs:
768 job_dict = job.get_object_dict()
769 job_dict['dependencies'] = ','.join(label.name
770 for label in job.dependencies)
showardc1a98d12010-01-15 00:22:22 +0000771 job_dict['keyvals'] = dict((keyval.key, keyval.value)
772 for keyval in job.keyvals)
Eric Lid23bc192011-02-09 14:38:57 -0800773 if job.parameterized_job:
774 job_dict['image'] = get_parameterized_autoupdate_image_url(job)
showard0957a842009-05-11 19:25:08 +0000775 job_dicts.append(job_dict)
776 return rpc_utils.prepare_for_serialization(job_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000777
778
779def get_num_jobs(not_yet_run=False, running=False, finished=False,
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700780 suite=False, sub=False, standalone=False,
jadmanski0afbb632008-06-06 21:10:57 +0000781 **filter_data):
782 """\
783 See get_jobs() for documentation of extra filter parameters.
784 """
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700785 extra_args = rpc_utils.extra_job_status_filters(not_yet_run,
786 running,
787 finished)
788 filter_data['extra_args'] = rpc_utils.extra_job_type_filters(extra_args,
789 suite,
790 sub,
791 standalone)
jadmanski0afbb632008-06-06 21:10:57 +0000792 return models.Job.query_count(filter_data)
mblighe8819cd2008-02-15 16:48:40 +0000793
794
mblighe8819cd2008-02-15 16:48:40 +0000795def get_jobs_summary(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000796 """\
Jiaxi Luoaac54572014-06-04 13:57:02 -0700797 Like get_jobs(), but adds 'status_counts' and 'result_counts' field.
798
799 'status_counts' filed is a dictionary mapping status strings to the number
800 of hosts currently with that status, i.e. {'Queued' : 4, 'Running' : 2}.
801
802 'result_counts' field is piped to tko's rpc_interface and has the return
803 format specified under get_group_counts.
jadmanski0afbb632008-06-06 21:10:57 +0000804 """
805 jobs = get_jobs(**filter_data)
806 ids = [job['id'] for job in jobs]
807 all_status_counts = models.Job.objects.get_status_counts(ids)
808 for job in jobs:
809 job['status_counts'] = all_status_counts[job['id']]
Jiaxi Luoaac54572014-06-04 13:57:02 -0700810 job['result_counts'] = tko_rpc_interface.get_status_counts(
811 ['afe_job_id', 'afe_job_id'],
812 header_groups=[['afe_job_id'], ['afe_job_id']],
813 **{'afe_job_id': job['id']})
jadmanski0afbb632008-06-06 21:10:57 +0000814 return rpc_utils.prepare_for_serialization(jobs)
mblighe8819cd2008-02-15 16:48:40 +0000815
816
showarda965cef2009-05-15 23:17:41 +0000817def get_info_for_clone(id, preserve_metahosts, queue_entry_filter_data=None):
showarda8709c52008-07-03 19:44:54 +0000818 """\
819 Retrieves all the information needed to clone a job.
820 """
showarda8709c52008-07-03 19:44:54 +0000821 job = models.Job.objects.get(id=id)
showard29f7cd22009-04-29 21:16:24 +0000822 job_info = rpc_utils.get_job_info(job,
showarda965cef2009-05-15 23:17:41 +0000823 preserve_metahosts,
824 queue_entry_filter_data)
showard945072f2008-09-03 20:34:59 +0000825
showardd9992fe2008-07-31 02:15:03 +0000826 host_dicts = []
showard29f7cd22009-04-29 21:16:24 +0000827 for host in job_info['hosts']:
828 host_dict = get_hosts(id=host.id)[0]
829 other_labels = host_dict['labels']
830 if host_dict['platform']:
831 other_labels.remove(host_dict['platform'])
832 host_dict['other_labels'] = ', '.join(other_labels)
showardd9992fe2008-07-31 02:15:03 +0000833 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +0000834
showard29f7cd22009-04-29 21:16:24 +0000835 for host in job_info['one_time_hosts']:
836 host_dict = dict(hostname=host.hostname,
837 id=host.id,
838 platform='(one-time host)',
839 locked_text='')
840 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +0000841
showard4d077562009-05-08 18:24:36 +0000842 # convert keys from Label objects to strings (names of labels)
showard29f7cd22009-04-29 21:16:24 +0000843 meta_host_counts = dict((meta_host.name, count) for meta_host, count
showard4d077562009-05-08 18:24:36 +0000844 in job_info['meta_host_counts'].iteritems())
showard29f7cd22009-04-29 21:16:24 +0000845
846 info = dict(job=job.get_object_dict(),
847 meta_host_counts=meta_host_counts,
848 hosts=host_dicts)
849 info['job']['dependencies'] = job_info['dependencies']
850 if job_info['atomic_group']:
851 info['atomic_group_name'] = (job_info['atomic_group']).name
852 else:
853 info['atomic_group_name'] = None
jamesren2275ef12010-04-12 18:25:06 +0000854 info['hostless'] = job_info['hostless']
jamesren76fcf192010-04-21 20:39:50 +0000855 info['drone_set'] = job.drone_set and job.drone_set.name
showarda8709c52008-07-03 19:44:54 +0000856
Eric Lid23bc192011-02-09 14:38:57 -0800857 if job.parameterized_job:
858 info['job']['image'] = get_parameterized_autoupdate_image_url(job)
859
showarda8709c52008-07-03 19:44:54 +0000860 return rpc_utils.prepare_for_serialization(info)
861
862
showard34dc5fa2008-04-24 20:58:40 +0000863# host queue entries
864
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700865def get_host_queue_entries(start_time=None, end_time=None, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000866 """\
showardc92da832009-04-07 18:14:34 +0000867 @returns A sequence of nested dictionaries of host and job information.
jadmanski0afbb632008-06-06 21:10:57 +0000868 """
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700869 filter_data = rpc_utils.inject_times_to_filter('started_on__gte',
870 'started_on__lte',
871 start_time,
872 end_time,
873 **filter_data)
showardc92da832009-04-07 18:14:34 +0000874 return rpc_utils.prepare_rows_as_nested_dicts(
875 models.HostQueueEntry.query_objects(filter_data),
876 ('host', 'atomic_group', 'job'))
showard34dc5fa2008-04-24 20:58:40 +0000877
878
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700879def get_num_host_queue_entries(start_time=None, end_time=None, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000880 """\
881 Get the number of host queue entries associated with this job.
882 """
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700883 filter_data = rpc_utils.inject_times_to_filter('started_on__gte',
884 'started_on__lte',
885 start_time,
886 end_time,
887 **filter_data)
jadmanski0afbb632008-06-06 21:10:57 +0000888 return models.HostQueueEntry.query_count(filter_data)
showard34dc5fa2008-04-24 20:58:40 +0000889
890
showard1e935f12008-07-11 00:11:36 +0000891def get_hqe_percentage_complete(**filter_data):
892 """
showardc92da832009-04-07 18:14:34 +0000893 Computes the fraction of host queue entries matching the given filter data
showard1e935f12008-07-11 00:11:36 +0000894 that are complete.
895 """
896 query = models.HostQueueEntry.query_objects(filter_data)
897 complete_count = query.filter(complete=True).count()
898 total_count = query.count()
899 if total_count == 0:
900 return 1
901 return float(complete_count) / total_count
902
903
showard1a5a4082009-07-28 20:01:37 +0000904# special tasks
905
906def get_special_tasks(**filter_data):
907 return rpc_utils.prepare_rows_as_nested_dicts(
908 models.SpecialTask.query_objects(filter_data),
909 ('host', 'queue_entry'))
910
911
showardc0ac3a72009-07-08 21:14:45 +0000912# support for host detail view
913
Jiaxi Luo79ce6422014-06-13 17:08:09 -0700914def get_host_queue_entries_and_special_tasks(host_id, query_start=None,
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700915 query_limit=None, start_time=None,
916 end_time=None):
showardc0ac3a72009-07-08 21:14:45 +0000917 """
918 @returns an interleaved list of HostQueueEntries and SpecialTasks,
919 in approximate run order. each dict contains keys for type, host,
920 job, status, started_on, execution_path, and ID.
921 """
922 total_limit = None
923 if query_limit is not None:
924 total_limit = query_start + query_limit
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700925 filter_data_common = {'host': host_id,
926 'query_limit': total_limit,
927 'sort_by': ['-id']}
showardc0ac3a72009-07-08 21:14:45 +0000928
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700929 filter_data_queue_entries, filter_data_special_tasks = (
930 rpc_utils.inject_times_to_hqe_special_tasks_filters(
931 filter_data_common, start_time, end_time))
932
933 queue_entries = list(models.HostQueueEntry.query_objects(
934 filter_data_queue_entries))
935 special_tasks = list(models.SpecialTask.query_objects(
936 filter_data_special_tasks))
showardc0ac3a72009-07-08 21:14:45 +0000937
938 interleaved_entries = rpc_utils.interleave_entries(queue_entries,
939 special_tasks)
940 if query_start is not None:
941 interleaved_entries = interleaved_entries[query_start:]
942 if query_limit is not None:
943 interleaved_entries = interleaved_entries[:query_limit]
944 return rpc_utils.prepare_for_serialization(interleaved_entries)
945
946
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700947def get_num_host_queue_entries_and_special_tasks(host_id, start_time=None,
948 end_time=None):
949 filter_data_common = {'host': host_id}
950
951 filter_data_queue_entries, filter_data_special_tasks = (
952 rpc_utils.inject_times_to_hqe_special_tasks_filters(
953 filter_data_common, start_time, end_time))
954
955 return (models.HostQueueEntry.query_count(filter_data_queue_entries)
956 + models.SpecialTask.query_count(filter_data_special_tasks))
showardc0ac3a72009-07-08 21:14:45 +0000957
958
showard29f7cd22009-04-29 21:16:24 +0000959# recurring run
960
961def get_recurring(**filter_data):
962 return rpc_utils.prepare_rows_as_nested_dicts(
963 models.RecurringRun.query_objects(filter_data),
964 ('job', 'owner'))
965
966
967def get_num_recurring(**filter_data):
968 return models.RecurringRun.query_count(filter_data)
969
970
971def delete_recurring_runs(**filter_data):
972 to_delete = models.RecurringRun.query_objects(filter_data)
973 to_delete.delete()
974
975
976def create_recurring_run(job_id, start_date, loop_period, loop_count):
showard64a95952010-01-13 21:27:16 +0000977 owner = models.User.current_user().login
showard29f7cd22009-04-29 21:16:24 +0000978 job = models.Job.objects.get(id=job_id)
979 return job.create_recurring_job(start_date=start_date,
980 loop_period=loop_period,
981 loop_count=loop_count,
982 owner=owner)
983
984
mblighe8819cd2008-02-15 16:48:40 +0000985# other
986
showarde0b63622008-08-04 20:58:47 +0000987def echo(data=""):
988 """\
989 Returns a passed in string. For doing a basic test to see if RPC calls
990 can successfully be made.
991 """
992 return data
993
994
showardb7a52fd2009-04-27 20:10:56 +0000995def get_motd():
996 """\
997 Returns the message of the day as a string.
998 """
999 return rpc_utils.get_motd()
1000
1001
mblighe8819cd2008-02-15 16:48:40 +00001002def get_static_data():
jadmanski0afbb632008-06-06 21:10:57 +00001003 """\
1004 Returns a dictionary containing a bunch of data that shouldn't change
1005 often and is otherwise inaccessible. This includes:
showardc92da832009-04-07 18:14:34 +00001006
1007 priorities: List of job priority choices.
1008 default_priority: Default priority value for new jobs.
1009 users: Sorted list of all users.
Jiaxi Luo31874592014-06-11 10:36:35 -07001010 labels: Sorted list of labels not start with 'cros-version' and
1011 'fw-version'.
showardc92da832009-04-07 18:14:34 +00001012 atomic_groups: Sorted list of all atomic groups.
1013 tests: Sorted list of all tests.
1014 profilers: Sorted list of all profilers.
1015 current_user: Logged-in username.
1016 host_statuses: Sorted list of possible Host statuses.
1017 job_statuses: Sorted list of possible HostQueueEntry statuses.
Simran Basi7e605742013-11-12 13:43:36 -08001018 job_timeout_default: The default job timeout length in minutes.
showarda1e74b32009-05-12 17:32:04 +00001019 parse_failed_repair_default: Default value for the parse_failed_repair job
Jiaxi Luo31874592014-06-11 10:36:35 -07001020 option.
showardc92da832009-04-07 18:14:34 +00001021 reboot_before_options: A list of valid RebootBefore string enums.
1022 reboot_after_options: A list of valid RebootAfter string enums.
1023 motd: Server's message of the day.
1024 status_dictionary: A mapping from one word job status names to a more
1025 informative description.
jadmanski0afbb632008-06-06 21:10:57 +00001026 """
showard21baa452008-10-21 00:08:39 +00001027
1028 job_fields = models.Job.get_field_dict()
jamesren76fcf192010-04-21 20:39:50 +00001029 default_drone_set_name = models.DroneSet.default_drone_set_name()
1030 drone_sets = ([default_drone_set_name] +
1031 sorted(drone_set.name for drone_set in
1032 models.DroneSet.objects.exclude(
1033 name=default_drone_set_name)))
showard21baa452008-10-21 00:08:39 +00001034
jadmanski0afbb632008-06-06 21:10:57 +00001035 result = {}
Alex Miller7d658cf2013-09-04 16:00:35 -07001036 result['priorities'] = priorities.Priority.choices()
1037 default_priority = priorities.Priority.DEFAULT
1038 result['default_priority'] = 'Default'
1039 result['max_schedulable_priority'] = priorities.Priority.DEFAULT
jadmanski0afbb632008-06-06 21:10:57 +00001040 result['users'] = get_users(sort_by=['login'])
Jiaxi Luo31874592014-06-11 10:36:35 -07001041
1042 label_exclude_filters = [{'name__startswith': 'cros-version'},
1043 {'name__startswith': 'fw-version'}]
1044 result['labels'] = get_labels(
1045 label_exclude_filters,
1046 sort_by=['-platform', 'name'])
1047
showardc92da832009-04-07 18:14:34 +00001048 result['atomic_groups'] = get_atomic_groups(sort_by=['name'])
jadmanski0afbb632008-06-06 21:10:57 +00001049 result['tests'] = get_tests(sort_by=['name'])
showard2b9a88b2008-06-13 20:55:03 +00001050 result['profilers'] = get_profilers(sort_by=['name'])
showard0fc38302008-10-23 00:44:07 +00001051 result['current_user'] = rpc_utils.prepare_for_serialization(
showard64a95952010-01-13 21:27:16 +00001052 models.User.current_user().get_object_dict())
showard2b9a88b2008-06-13 20:55:03 +00001053 result['host_statuses'] = sorted(models.Host.Status.names)
mbligh5a198b92008-12-11 19:33:29 +00001054 result['job_statuses'] = sorted(models.HostQueueEntry.Status.names)
Simran Basi7e605742013-11-12 13:43:36 -08001055 result['job_timeout_mins_default'] = models.Job.DEFAULT_TIMEOUT_MINS
Simran Basi34217022012-11-06 13:43:15 -08001056 result['job_max_runtime_mins_default'] = (
1057 models.Job.DEFAULT_MAX_RUNTIME_MINS)
showarda1e74b32009-05-12 17:32:04 +00001058 result['parse_failed_repair_default'] = bool(
1059 models.Job.DEFAULT_PARSE_FAILED_REPAIR)
jamesrendd855242010-03-02 22:23:44 +00001060 result['reboot_before_options'] = model_attributes.RebootBefore.names
1061 result['reboot_after_options'] = model_attributes.RebootAfter.names
showard8fbae652009-01-20 23:23:10 +00001062 result['motd'] = rpc_utils.get_motd()
jamesren76fcf192010-04-21 20:39:50 +00001063 result['drone_sets_enabled'] = models.DroneSet.drone_sets_enabled()
1064 result['drone_sets'] = drone_sets
jamesren4a41e012010-07-16 22:33:48 +00001065 result['parameterized_jobs'] = models.Job.parameterized_jobs_enabled()
showard8ac29b42008-07-17 17:01:55 +00001066
showardd3dc1992009-04-22 21:01:40 +00001067 result['status_dictionary'] = {"Aborted": "Aborted",
showard8ac29b42008-07-17 17:01:55 +00001068 "Verifying": "Verifying Host",
Alex Millerdfff2fd2013-05-28 13:05:06 -07001069 "Provisioning": "Provisioning Host",
showard8ac29b42008-07-17 17:01:55 +00001070 "Pending": "Waiting on other hosts",
1071 "Running": "Running autoserv",
1072 "Completed": "Autoserv completed",
1073 "Failed": "Failed to complete",
showardd823b362008-07-24 16:35:46 +00001074 "Queued": "Queued",
showard5deb6772008-11-04 21:54:33 +00001075 "Starting": "Next in host's queue",
1076 "Stopped": "Other host(s) failed verify",
showardd3dc1992009-04-22 21:01:40 +00001077 "Parsing": "Awaiting parse of final results",
showard29f7cd22009-04-29 21:16:24 +00001078 "Gathering": "Gathering log files",
showard8cc058f2009-09-08 16:26:33 +00001079 "Template": "Template job for recurring run",
mbligh4608b002010-01-05 18:22:35 +00001080 "Waiting": "Waiting for scheduler action",
Dan Shi07e09af2013-04-12 09:31:29 -07001081 "Archiving": "Archiving results",
1082 "Resetting": "Resetting hosts"}
Jiaxi Luo421608e2014-07-07 14:38:00 -07001083
1084 result['wmatrix_url'] = rpc_utils.get_wmatrix_url()
Simran Basi71206ef2014-08-13 13:51:18 -07001085 result['is_moblab'] = bool(utils.is_moblab())
Jiaxi Luo421608e2014-07-07 14:38:00 -07001086
jadmanski0afbb632008-06-06 21:10:57 +00001087 return result
showard29f7cd22009-04-29 21:16:24 +00001088
1089
1090def get_server_time():
1091 return datetime.datetime.now().strftime("%Y-%m-%d %H:%M")