blob: a21bd0620cca3fdcd93da14073fb47bf1ace892f [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
Prashanth Balasubramanian5949b4a2014-11-23 12:58:30 -080069@rpc_utils.forward_multi_host_rpc_to_shards
showardbbabf502008-06-06 00:02:02 +000070def label_add_hosts(id, hosts):
Prashanth Balasubramanian5949b4a2014-11-23 12:58:30 -080071 """Add the label with the given id to the list of hosts.
72
73 The given label will be created if it doesn't exist, provided the `id`
74 supplied is a label name not an int/long id.
75
76 @param id: An id or label name. More often a label name.
77 @param hosts: A list of hostnames or ids. More often hostnames.
78
79 @raises models.Label.DoesNotExist: If the id specified is an int/long
80 and a label with that id doesn't exist.
81 """
showardbe3ec042008-11-12 18:16:07 +000082 host_objs = models.Host.smart_get_bulk(hosts)
Prashanth Balasubramanian5949b4a2014-11-23 12:58:30 -080083 try:
84 # In the rare event that we're given an id and not a label name,
85 # it should already exist.
86 label = models.Label.smart_get(id)
87 except models.Label.DoesNotExist:
88 # This matches the type checks in smart_get, which is a hack
89 # in and off itself. The aim here is to create any non-existent
90 # label, which we cannot do if the 'id' specified isn't a label name.
91 if isinstance(id, basestring):
92 label = models.Label.smart_get(add_label(id))
93 else:
94 raise
95
showardcafd16e2009-05-29 18:37:49 +000096 if label.platform:
97 models.Host.check_no_platform(host_objs)
98 label.host_set.add(*host_objs)
showardbbabf502008-06-06 00:02:02 +000099
100
Prashanth Balasubramanian5949b4a2014-11-23 12:58:30 -0800101@rpc_utils.forward_multi_host_rpc_to_shards
showardbbabf502008-06-06 00:02:02 +0000102def label_remove_hosts(id, hosts):
showardbe3ec042008-11-12 18:16:07 +0000103 host_objs = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000104 models.Label.smart_get(id).host_set.remove(*host_objs)
showardbbabf502008-06-06 00:02:02 +0000105
106
Jiaxi Luo31874592014-06-11 10:36:35 -0700107def get_labels(exclude_filters=(), **filter_data):
showardc92da832009-04-07 18:14:34 +0000108 """\
Jiaxi Luo31874592014-06-11 10:36:35 -0700109 @param exclude_filters: A sequence of dictionaries of filters.
110
showardc92da832009-04-07 18:14:34 +0000111 @returns A sequence of nested dictionaries of label information.
112 """
Jiaxi Luo31874592014-06-11 10:36:35 -0700113 labels = models.Label.query_objects(filter_data)
114 for exclude_filter in exclude_filters:
115 labels = labels.exclude(**exclude_filter)
116 return rpc_utils.prepare_rows_as_nested_dicts(labels, ('atomic_group',))
showardc92da832009-04-07 18:14:34 +0000117
118
119# atomic groups
120
showarde9450c92009-06-30 01:58:52 +0000121def add_atomic_group(name, max_number_of_machines=None, description=None):
showardc92da832009-04-07 18:14:34 +0000122 return models.AtomicGroup.add_object(
123 name=name, max_number_of_machines=max_number_of_machines,
124 description=description).id
125
126
127def modify_atomic_group(id, **data):
128 models.AtomicGroup.smart_get(id).update_object(data)
129
130
131def delete_atomic_group(id):
132 models.AtomicGroup.smart_get(id).delete()
133
134
135def atomic_group_add_labels(id, labels):
136 label_objs = models.Label.smart_get_bulk(labels)
137 models.AtomicGroup.smart_get(id).label_set.add(*label_objs)
138
139
140def atomic_group_remove_labels(id, labels):
141 label_objs = models.Label.smart_get_bulk(labels)
142 models.AtomicGroup.smart_get(id).label_set.remove(*label_objs)
143
144
145def get_atomic_groups(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000146 return rpc_utils.prepare_for_serialization(
showardc92da832009-04-07 18:14:34 +0000147 models.AtomicGroup.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000148
149
150# hosts
151
showarddf062562008-07-03 19:56:37 +0000152def add_host(hostname, status=None, locked=None, protection=None):
jadmanski0afbb632008-06-06 21:10:57 +0000153 return models.Host.add_object(hostname=hostname, status=status,
showarddf062562008-07-03 19:56:37 +0000154 locked=locked, protection=protection).id
mblighe8819cd2008-02-15 16:48:40 +0000155
156
Jakob Juelich50e91f72014-10-01 12:43:23 -0700157@rpc_utils.forward_single_host_rpc_to_shard
mblighe8819cd2008-02-15 16:48:40 +0000158def modify_host(id, **data):
Jakob Juelich50e91f72014-10-01 12:43:23 -0700159 """Modify local attributes of a host.
160
161 If this is called on the master, but the host is assigned to a shard, this
162 will also forward the call to the responsible shard. This means i.e. if a
163 host is being locked using this function, this change will also propagate to
164 shards.
165
166 @param id: id of the host to modify.
167 @param **data: key=value pairs of values to set on the host.
168 """
showardbe0d8692009-08-20 23:42:44 +0000169 rpc_utils.check_modify_host(data)
showardce7c0922009-09-11 18:39:24 +0000170 host = models.Host.smart_get(id)
Jakob Juelich50e91f72014-10-01 12:43:23 -0700171
showardce7c0922009-09-11 18:39:24 +0000172 rpc_utils.check_modify_host_locking(host, data)
173 host.update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000174
175
showard276f9442009-05-20 00:33:16 +0000176def modify_hosts(host_filter_data, update_data):
Jakob Juelich50e91f72014-10-01 12:43:23 -0700177 """Modify local attributes of multiple hosts.
178
179 If this is called on the master, but one of the hosts in that match the
180 filters is assigned to a shard, this will also forward the call to the
181 responsible shard.
182
183 The filters are always applied on the master, not on the shards. This means
184 if the states of a host differ on the master and a shard, the state on the
185 master will be used. I.e. this means:
186 A host was synced to Shard 1. On Shard 1 the status of the host was set to
187 'Repair Failed'.
188 - A call to modify_hosts with host_filter_data={'status': 'Ready'} will
189 update the host (both on the shard and on the master), because the state
190 of the host as the master knows it is still 'Ready'.
191 - A call to modify_hosts with host_filter_data={'status': 'Repair failed'
192 will not update the host, because the filter doesn't apply on the master.
193
showardbe0d8692009-08-20 23:42:44 +0000194 @param host_filter_data: Filters out which hosts to modify.
195 @param update_data: A dictionary with the changes to make to the hosts.
showard276f9442009-05-20 00:33:16 +0000196 """
showardbe0d8692009-08-20 23:42:44 +0000197 rpc_utils.check_modify_host(update_data)
showard276f9442009-05-20 00:33:16 +0000198 hosts = models.Host.query_objects(host_filter_data)
Jakob Juelich50e91f72014-10-01 12:43:23 -0700199
200 affected_shard_hostnames = set()
201 affected_host_ids = []
202
Alex Miller9658a952013-05-14 16:40:02 -0700203 # Check all hosts before changing data for exception safety.
204 for host in hosts:
205 rpc_utils.check_modify_host_locking(host, update_data)
Jakob Juelich50e91f72014-10-01 12:43:23 -0700206 if host.shard:
207 affected_shard_hostnames.add(host.shard.hostname)
208 affected_host_ids.append(host.id)
209
210 if not rpc_utils.is_shard():
211 # Caution: Changing the filter from the original here. See docstring.
212 rpc_utils.run_rpc_on_multiple_hostnames(
213 'modify_hosts', affected_shard_hostnames,
214 host_filter_data={'id__in': affected_host_ids},
215 update_data=update_data)
216
showard276f9442009-05-20 00:33:16 +0000217 for host in hosts:
218 host.update_object(update_data)
219
220
mblighe8819cd2008-02-15 16:48:40 +0000221def host_add_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000222 labels = models.Label.smart_get_bulk(labels)
showardcafd16e2009-05-29 18:37:49 +0000223 host = models.Host.smart_get(id)
224
225 platforms = [label.name for label in labels if label.platform]
226 if len(platforms) > 1:
227 raise model_logic.ValidationError(
228 {'labels': 'Adding more than one platform label: %s' %
229 ', '.join(platforms)})
230 if len(platforms) == 1:
231 models.Host.check_no_platform([host])
232 host.labels.add(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000233
234
235def host_remove_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000236 labels = models.Label.smart_get_bulk(labels)
jadmanski0afbb632008-06-06 21:10:57 +0000237 models.Host.smart_get(id).labels.remove(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000238
239
MK Ryuacf35922014-10-03 14:56:49 -0700240def get_host_attribute(attribute, **host_filter_data):
241 """
242 @param attribute: string name of attribute
243 @param host_filter_data: filter data to apply to Hosts to choose hosts to
244 act upon
245 """
246 hosts = rpc_utils.get_host_query((), False, False, True, host_filter_data)
247 hosts = list(hosts)
248 models.Host.objects.populate_relationships(hosts, models.HostAttribute,
249 'attribute_list')
250 host_attr_dicts = []
251 for host_obj in hosts:
252 for attr_obj in host_obj.attribute_list:
253 if attr_obj.attribute == attribute:
254 host_attr_dicts.append(attr_obj.get_object_dict())
255 return rpc_utils.prepare_for_serialization(host_attr_dicts)
256
257
showard0957a842009-05-11 19:25:08 +0000258def set_host_attribute(attribute, value, **host_filter_data):
259 """
260 @param attribute string name of attribute
261 @param value string, or None to delete an attribute
262 @param host_filter_data filter data to apply to Hosts to choose hosts to act
263 upon
264 """
265 assert host_filter_data # disallow accidental actions on all hosts
266 hosts = models.Host.query_objects(host_filter_data)
267 models.AclGroup.check_for_acl_violation_hosts(hosts)
268
269 for host in hosts:
showardf8b19042009-05-12 17:22:49 +0000270 host.set_or_delete_attribute(attribute, value)
showard0957a842009-05-11 19:25:08 +0000271
272
Jakob Juelich50e91f72014-10-01 12:43:23 -0700273@rpc_utils.forward_single_host_rpc_to_shard
mblighe8819cd2008-02-15 16:48:40 +0000274def delete_host(id):
jadmanski0afbb632008-06-06 21:10:57 +0000275 models.Host.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000276
277
showard87cc38f2009-08-20 23:37:04 +0000278def get_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000279 exclude_atomic_group_hosts=False, valid_only=True, **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000280 """
281 @param multiple_labels: match hosts in all of the labels given. Should
282 be a list of label names.
283 @param exclude_only_if_needed_labels: Exclude hosts with at least one
284 "only_if_needed" label applied.
285 @param exclude_atomic_group_hosts: Exclude hosts that have one or more
286 atomic group labels associated with them.
jadmanski0afbb632008-06-06 21:10:57 +0000287 """
showard43a3d262008-11-12 18:17:05 +0000288 hosts = rpc_utils.get_host_query(multiple_labels,
289 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000290 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000291 valid_only, filter_data)
showard0957a842009-05-11 19:25:08 +0000292 hosts = list(hosts)
293 models.Host.objects.populate_relationships(hosts, models.Label,
294 'label_list')
295 models.Host.objects.populate_relationships(hosts, models.AclGroup,
296 'acl_list')
297 models.Host.objects.populate_relationships(hosts, models.HostAttribute,
298 'attribute_list')
showard43a3d262008-11-12 18:17:05 +0000299 host_dicts = []
300 for host_obj in hosts:
301 host_dict = host_obj.get_object_dict()
showard0957a842009-05-11 19:25:08 +0000302 host_dict['labels'] = [label.name for label in host_obj.label_list]
showard909c9142009-07-07 20:54:42 +0000303 host_dict['platform'], host_dict['atomic_group'] = (rpc_utils.
304 find_platform_and_atomic_group(host_obj))
showard0957a842009-05-11 19:25:08 +0000305 host_dict['acls'] = [acl.name for acl in host_obj.acl_list]
306 host_dict['attributes'] = dict((attribute.attribute, attribute.value)
307 for attribute in host_obj.attribute_list)
showard43a3d262008-11-12 18:17:05 +0000308 host_dicts.append(host_dict)
309 return rpc_utils.prepare_for_serialization(host_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000310
311
showard87cc38f2009-08-20 23:37:04 +0000312def get_num_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000313 exclude_atomic_group_hosts=False, valid_only=True,
314 **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000315 """
316 Same parameters as get_hosts().
317
318 @returns The number of matching hosts.
319 """
showard43a3d262008-11-12 18:17:05 +0000320 hosts = rpc_utils.get_host_query(multiple_labels,
321 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000322 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000323 valid_only, filter_data)
showard43a3d262008-11-12 18:17:05 +0000324 return hosts.count()
showard1385b162008-03-13 15:59:40 +0000325
mblighe8819cd2008-02-15 16:48:40 +0000326
327# tests
328
showard909c7a62008-07-15 21:52:38 +0000329def add_test(name, test_type, path, author=None, dependencies=None,
showard3d9899a2008-07-31 02:11:58 +0000330 experimental=True, run_verify=None, test_class=None,
showard909c7a62008-07-15 21:52:38 +0000331 test_time=None, test_category=None, description=None,
332 sync_count=1):
jadmanski0afbb632008-06-06 21:10:57 +0000333 return models.Test.add_object(name=name, test_type=test_type, path=path,
showard909c7a62008-07-15 21:52:38 +0000334 author=author, dependencies=dependencies,
335 experimental=experimental,
336 run_verify=run_verify, test_time=test_time,
337 test_category=test_category,
338 sync_count=sync_count,
jadmanski0afbb632008-06-06 21:10:57 +0000339 test_class=test_class,
340 description=description).id
mblighe8819cd2008-02-15 16:48:40 +0000341
342
343def modify_test(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000344 models.Test.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000345
346
347def delete_test(id):
jadmanski0afbb632008-06-06 21:10:57 +0000348 models.Test.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000349
350
351def get_tests(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000352 return rpc_utils.prepare_for_serialization(
353 models.Test.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000354
355
showard2b9a88b2008-06-13 20:55:03 +0000356# profilers
357
358def add_profiler(name, description=None):
359 return models.Profiler.add_object(name=name, description=description).id
360
361
362def modify_profiler(id, **data):
363 models.Profiler.smart_get(id).update_object(data)
364
365
366def delete_profiler(id):
367 models.Profiler.smart_get(id).delete()
368
369
370def get_profilers(**filter_data):
371 return rpc_utils.prepare_for_serialization(
372 models.Profiler.list_objects(filter_data))
373
374
mblighe8819cd2008-02-15 16:48:40 +0000375# users
376
377def add_user(login, access_level=None):
jadmanski0afbb632008-06-06 21:10:57 +0000378 return models.User.add_object(login=login, access_level=access_level).id
mblighe8819cd2008-02-15 16:48:40 +0000379
380
381def modify_user(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000382 models.User.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000383
384
385def delete_user(id):
jadmanski0afbb632008-06-06 21:10:57 +0000386 models.User.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000387
388
389def get_users(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000390 return rpc_utils.prepare_for_serialization(
391 models.User.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000392
393
394# acl groups
395
396def add_acl_group(name, description=None):
showard04f2cd82008-07-25 20:53:31 +0000397 group = models.AclGroup.add_object(name=name, description=description)
showard64a95952010-01-13 21:27:16 +0000398 group.users.add(models.User.current_user())
showard04f2cd82008-07-25 20:53:31 +0000399 return group.id
mblighe8819cd2008-02-15 16:48:40 +0000400
401
402def modify_acl_group(id, **data):
showard04f2cd82008-07-25 20:53:31 +0000403 group = models.AclGroup.smart_get(id)
404 group.check_for_acl_violation_acl_group()
405 group.update_object(data)
406 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000407
408
409def acl_group_add_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000410 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000411 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000412 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000413 group.users.add(*users)
mblighe8819cd2008-02-15 16:48:40 +0000414
415
416def acl_group_remove_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000417 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000418 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000419 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000420 group.users.remove(*users)
showard04f2cd82008-07-25 20:53:31 +0000421 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000422
423
424def acl_group_add_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000425 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000426 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000427 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000428 group.hosts.add(*hosts)
showard08f981b2008-06-24 21:59:03 +0000429 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000430
431
432def acl_group_remove_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000433 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000434 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000435 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000436 group.hosts.remove(*hosts)
showard08f981b2008-06-24 21:59:03 +0000437 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000438
439
440def delete_acl_group(id):
jadmanski0afbb632008-06-06 21:10:57 +0000441 models.AclGroup.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000442
443
444def get_acl_groups(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000445 acl_groups = models.AclGroup.list_objects(filter_data)
446 for acl_group in acl_groups:
447 acl_group_obj = models.AclGroup.objects.get(id=acl_group['id'])
448 acl_group['users'] = [user.login
449 for user in acl_group_obj.users.all()]
450 acl_group['hosts'] = [host.hostname
451 for host in acl_group_obj.hosts.all()]
452 return rpc_utils.prepare_for_serialization(acl_groups)
mblighe8819cd2008-02-15 16:48:40 +0000453
454
455# jobs
456
mbligh120351e2009-01-24 01:40:45 +0000457def generate_control_file(tests=(), kernel=None, label=None, profilers=(),
showard91f85102009-10-12 20:34:52 +0000458 client_control_file='', use_container=False,
showard232b7ae2009-11-10 00:46:48 +0000459 profile_only=None, upload_kernel_config=False):
jadmanski0afbb632008-06-06 21:10:57 +0000460 """
mbligh120351e2009-01-24 01:40:45 +0000461 Generates a client-side control file to load a kernel and run tests.
462
463 @param tests List of tests to run.
mbligha3c58d22009-08-24 22:01:51 +0000464 @param kernel A list of kernel info dictionaries configuring which kernels
465 to boot for this job and other options for them
mbligh120351e2009-01-24 01:40:45 +0000466 @param label Name of label to grab kernel config from.
467 @param profilers List of profilers to activate during the job.
468 @param client_control_file The contents of a client-side control file to
469 run at the end of all tests. If this is supplied, all tests must be
470 client side.
471 TODO: in the future we should support server control files directly
472 to wrap with a kernel. That'll require changing the parameter
473 name and adding a boolean to indicate if it is a client or server
474 control file.
475 @param use_container unused argument today. TODO: Enable containers
476 on the host during a client side test.
showard91f85102009-10-12 20:34:52 +0000477 @param profile_only A boolean that indicates what default profile_only
478 mode to use in the control file. Passing None will generate a
479 control file that does not explcitly set the default mode at all.
showard232b7ae2009-11-10 00:46:48 +0000480 @param upload_kernel_config: if enabled it will generate server control
481 file code that uploads the kernel config file to the client and
482 tells the client of the new (local) path when compiling the kernel;
483 the tests must be server side tests
mbligh120351e2009-01-24 01:40:45 +0000484
485 @returns a dict with the following keys:
486 control_file: str, The control file text.
487 is_server: bool, is the control file a server-side control file?
488 synch_count: How many machines the job uses per autoserv execution.
489 synch_count == 1 means the job is asynchronous.
490 dependencies: A list of the names of labels on which the job depends.
491 """
showardd86debe2009-06-10 17:37:56 +0000492 if not tests and not client_control_file:
showard2bab8f42008-11-12 18:15:22 +0000493 return dict(control_file='', is_server=False, synch_count=1,
showard989f25d2008-10-01 11:38:11 +0000494 dependencies=[])
mblighe8819cd2008-02-15 16:48:40 +0000495
showard989f25d2008-10-01 11:38:11 +0000496 cf_info, test_objects, profiler_objects, label = (
showard2b9a88b2008-06-13 20:55:03 +0000497 rpc_utils.prepare_generate_control_file(tests, kernel, label,
498 profilers))
showard989f25d2008-10-01 11:38:11 +0000499 cf_info['control_file'] = control_file.generate_control(
mbligha3c58d22009-08-24 22:01:51 +0000500 tests=test_objects, kernels=kernel, platform=label,
mbligh120351e2009-01-24 01:40:45 +0000501 profilers=profiler_objects, is_server=cf_info['is_server'],
showard232b7ae2009-11-10 00:46:48 +0000502 client_control_file=client_control_file, profile_only=profile_only,
503 upload_kernel_config=upload_kernel_config)
showard989f25d2008-10-01 11:38:11 +0000504 return cf_info
mblighe8819cd2008-02-15 16:48:40 +0000505
506
jamesren4a41e012010-07-16 22:33:48 +0000507def create_parameterized_job(name, priority, test, parameters, kernel=None,
508 label=None, profilers=(), profiler_parameters=None,
509 use_container=False, profile_only=None,
510 upload_kernel_config=False, hosts=(),
511 meta_hosts=(), one_time_hosts=(),
512 atomic_group_name=None, synch_count=None,
513 is_template=False, timeout=None,
Simran Basi7e605742013-11-12 13:43:36 -0800514 timeout_mins=None, max_runtime_mins=None,
515 run_verify=False, email_list='', dependencies=(),
516 reboot_before=None, reboot_after=None,
517 parse_failed_repair=None, hostless=False,
518 keyvals=None, drone_set=None, run_reset=True):
jamesren4a41e012010-07-16 22:33:48 +0000519 """
520 Creates and enqueues a parameterized job.
521
522 Most parameters a combination of the parameters for generate_control_file()
523 and create_job(), with the exception of:
524
525 @param test name or ID of the test to run
526 @param parameters a map of parameter name ->
527 tuple of (param value, param type)
528 @param profiler_parameters a dictionary of parameters for the profilers:
529 key: profiler name
530 value: dict of param name -> tuple of
531 (param value,
532 param type)
533 """
534 # Save the values of the passed arguments here. What we're going to do with
535 # them is pass them all to rpc_utils.get_create_job_common_args(), which
536 # will extract the subset of these arguments that apply for
537 # rpc_utils.create_job_common(), which we then pass in to that function.
538 args = locals()
539
540 # Set up the parameterized job configs
541 test_obj = models.Test.smart_get(test)
Aviv Keshet3dd8beb2013-05-13 17:36:04 -0700542 control_type = test_obj.test_type
jamesren4a41e012010-07-16 22:33:48 +0000543
544 try:
545 label = models.Label.smart_get(label)
546 except models.Label.DoesNotExist:
547 label = None
548
549 kernel_objs = models.Kernel.create_kernels(kernel)
550 profiler_objs = [models.Profiler.smart_get(profiler)
551 for profiler in profilers]
552
553 parameterized_job = models.ParameterizedJob.objects.create(
554 test=test_obj, label=label, use_container=use_container,
555 profile_only=profile_only,
556 upload_kernel_config=upload_kernel_config)
557 parameterized_job.kernels.add(*kernel_objs)
558
559 for profiler in profiler_objs:
560 parameterized_profiler = models.ParameterizedJobProfiler.objects.create(
561 parameterized_job=parameterized_job,
562 profiler=profiler)
563 profiler_params = profiler_parameters.get(profiler.name, {})
564 for name, (value, param_type) in profiler_params.iteritems():
565 models.ParameterizedJobProfilerParameter.objects.create(
566 parameterized_job_profiler=parameterized_profiler,
567 parameter_name=name,
568 parameter_value=value,
569 parameter_type=param_type)
570
571 try:
572 for parameter in test_obj.testparameter_set.all():
573 if parameter.name in parameters:
574 param_value, param_type = parameters.pop(parameter.name)
575 parameterized_job.parameterizedjobparameter_set.create(
576 test_parameter=parameter, parameter_value=param_value,
577 parameter_type=param_type)
578
579 if parameters:
580 raise Exception('Extra parameters remain: %r' % parameters)
581
582 return rpc_utils.create_job_common(
583 parameterized_job=parameterized_job.id,
584 control_type=control_type,
585 **rpc_utils.get_create_job_common_args(args))
586 except:
587 parameterized_job.delete()
588 raise
589
590
Simran Basib6ec8ae2014-04-23 12:05:08 -0700591def create_job_page_handler(name, priority, control_file, control_type,
592 image=None, hostless=False, **kwargs):
593 """\
594 Create and enqueue a job.
595
596 @param name name of this job
597 @param priority Integer priority of this job. Higher is more important.
598 @param control_file String contents of the control file.
599 @param control_type Type of control file, Client or Server.
600 @param kwargs extra args that will be required by create_suite_job or
601 create_job.
602
603 @returns The created Job id number.
604 """
605 control_file = rpc_utils.encode_ascii(control_file)
Jiaxi Luodd67beb2014-07-18 16:28:31 -0700606 if not control_file:
607 raise model_logic.ValidationError({
608 'control_file' : "Control file cannot be empty"})
Simran Basib6ec8ae2014-04-23 12:05:08 -0700609
610 if image and hostless:
611 return site_rpc_interface.create_suite_job(
612 name=name, control_file=control_file, priority=priority,
613 build=image, **kwargs)
614 return create_job(name, priority, control_file, control_type, image=image,
615 hostless=hostless, **kwargs)
616
617
showard12f3e322009-05-13 21:27:42 +0000618def create_job(name, priority, control_file, control_type,
619 hosts=(), meta_hosts=(), one_time_hosts=(),
620 atomic_group_name=None, synch_count=None, is_template=False,
Simran Basi7e605742013-11-12 13:43:36 -0800621 timeout=None, timeout_mins=None, max_runtime_mins=None,
622 run_verify=False, email_list='', dependencies=(),
623 reboot_before=None, reboot_after=None, parse_failed_repair=None,
624 hostless=False, keyvals=None, drone_set=None, image=None,
Jiaxi Luo90190c92014-06-18 12:35:57 -0700625 parent_job_id=None, test_retry=0, run_reset=True, args=(),
626 **kwargs):
jadmanski0afbb632008-06-06 21:10:57 +0000627 """\
628 Create and enqueue a job.
mblighe8819cd2008-02-15 16:48:40 +0000629
showarda1e74b32009-05-12 17:32:04 +0000630 @param name name of this job
Alex Miller7d658cf2013-09-04 16:00:35 -0700631 @param priority Integer priority of this job. Higher is more important.
showarda1e74b32009-05-12 17:32:04 +0000632 @param control_file String contents of the control file.
633 @param control_type Type of control file, Client or Server.
634 @param synch_count How many machines the job uses per autoserv execution.
Jiaxi Luo90190c92014-06-18 12:35:57 -0700635 synch_count == 1 means the job is asynchronous. If an atomic group is
636 given this value is treated as a minimum.
showarda1e74b32009-05-12 17:32:04 +0000637 @param is_template If true then create a template job.
638 @param timeout Hours after this call returns until the job times out.
Simran Basi7e605742013-11-12 13:43:36 -0800639 @param timeout_mins Minutes after this call returns until the job times
Jiaxi Luo90190c92014-06-18 12:35:57 -0700640 out.
Simran Basi34217022012-11-06 13:43:15 -0800641 @param max_runtime_mins Minutes from job starting time until job times out
showarda1e74b32009-05-12 17:32:04 +0000642 @param run_verify Should the host be verified before running the test?
643 @param email_list String containing emails to mail when the job is done
644 @param dependencies List of label names on which this job depends
645 @param reboot_before Never, If dirty, or Always
646 @param reboot_after Never, If all tests passed, or Always
647 @param parse_failed_repair if true, results of failed repairs launched by
Jiaxi Luo90190c92014-06-18 12:35:57 -0700648 this job will be parsed as part of the job.
showarda9545c02009-12-18 22:44:26 +0000649 @param hostless if true, create a hostless job
showardc1a98d12010-01-15 00:22:22 +0000650 @param keyvals dict of keyvals to associate with the job
showarda1e74b32009-05-12 17:32:04 +0000651 @param hosts List of hosts to run job on.
652 @param meta_hosts List where each entry is a label name, and for each entry
Jiaxi Luo90190c92014-06-18 12:35:57 -0700653 one host will be chosen from that label to run the job on.
showarda1e74b32009-05-12 17:32:04 +0000654 @param one_time_hosts List of hosts not in the database to run the job on.
655 @param atomic_group_name The name of an atomic group to schedule the job on.
jamesren76fcf192010-04-21 20:39:50 +0000656 @param drone_set The name of the drone set to run this test on.
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800657 @param image OS image to install before running job.
Aviv Keshet0b9cfc92013-02-05 11:36:02 -0800658 @param parent_job_id id of a job considered to be parent of created job.
Simran Basib6ec8ae2014-04-23 12:05:08 -0700659 @param test_retry Number of times to retry test if the test did not
Jiaxi Luo90190c92014-06-18 12:35:57 -0700660 complete successfully. (optional, default: 0)
Simran Basib6ec8ae2014-04-23 12:05:08 -0700661 @param run_reset Should the host be reset before running the test?
Jiaxi Luo90190c92014-06-18 12:35:57 -0700662 @param args A list of args to be injected into control file.
Simran Basib6ec8ae2014-04-23 12:05:08 -0700663 @param kwargs extra keyword args. NOT USED.
showardc92da832009-04-07 18:14:34 +0000664
665 @returns The created Job id number.
jadmanski0afbb632008-06-06 21:10:57 +0000666 """
Jiaxi Luo90190c92014-06-18 12:35:57 -0700667 if args:
668 control_file = tools.inject_vars({'args': args}, control_file)
669
Simran Basiab5a1bf2014-05-28 15:39:44 -0700670 if image is None:
671 return rpc_utils.create_job_common(
672 **rpc_utils.get_create_job_common_args(locals()))
673
674 # When image is supplied use a known parameterized test already in the
675 # database to pass the OS image path from the front end, through the
676 # scheduler, and finally to autoserv as the --image parameter.
677
678 # The test autoupdate_ParameterizedJob is in afe_autotests and used to
679 # instantiate a Test object and from there a ParameterizedJob.
680 known_test_obj = models.Test.smart_get('autoupdate_ParameterizedJob')
681 known_parameterized_job = models.ParameterizedJob.objects.create(
682 test=known_test_obj)
683
684 # autoupdate_ParameterizedJob has a single parameter, the image parameter,
685 # stored in the table afe_test_parameters. We retrieve and set this
686 # instance of the parameter to the OS image path.
687 image_parameter = known_test_obj.testparameter_set.get(test=known_test_obj,
688 name='image')
689 known_parameterized_job.parameterizedjobparameter_set.create(
690 test_parameter=image_parameter, parameter_value=image,
691 parameter_type='string')
692
693 # By passing a parameterized_job to create_job_common the job entry in
694 # the afe_jobs table will have the field parameterized_job_id set.
695 # The scheduler uses this id in the afe_parameterized_jobs table to
696 # match this job to our known test, and then with the
697 # afe_parameterized_job_parameters table to get the actual image path.
jamesren4a41e012010-07-16 22:33:48 +0000698 return rpc_utils.create_job_common(
Simran Basiab5a1bf2014-05-28 15:39:44 -0700699 parameterized_job=known_parameterized_job.id,
jamesren4a41e012010-07-16 22:33:48 +0000700 **rpc_utils.get_create_job_common_args(locals()))
mblighe8819cd2008-02-15 16:48:40 +0000701
702
showard9dbdcda2008-10-14 17:34:36 +0000703def abort_host_queue_entries(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000704 """\
showard9dbdcda2008-10-14 17:34:36 +0000705 Abort a set of host queue entries.
jadmanski0afbb632008-06-06 21:10:57 +0000706 """
showard9dbdcda2008-10-14 17:34:36 +0000707 query = models.HostQueueEntry.query_objects(filter_data)
beepsfaecbce2013-10-29 11:35:10 -0700708
709 # Dont allow aborts on:
710 # 1. Jobs that have already completed (whether or not they were aborted)
711 # 2. Jobs that we have already been aborted (but may not have completed)
712 query = query.filter(complete=False).filter(aborted=False)
showarddc817512008-11-12 18:16:41 +0000713 models.AclGroup.check_abort_permissions(query)
showard9dbdcda2008-10-14 17:34:36 +0000714 host_queue_entries = list(query.select_related())
showard2bab8f42008-11-12 18:15:22 +0000715 rpc_utils.check_abort_synchronous_jobs(host_queue_entries)
mblighe8819cd2008-02-15 16:48:40 +0000716
Simran Basic1b26762013-06-26 14:23:21 -0700717 models.HostQueueEntry.abort_host_queue_entries(host_queue_entries)
showard9d821ab2008-07-11 16:54:29 +0000718
719
beeps8bb1f7d2013-08-05 01:30:09 -0700720def abort_special_tasks(**filter_data):
721 """\
722 Abort the special task, or tasks, specified in the filter.
723 """
724 query = models.SpecialTask.query_objects(filter_data)
725 special_tasks = query.filter(is_active=True)
726 for task in special_tasks:
727 task.abort()
728
729
Simran Basi73dae552013-02-25 14:57:46 -0800730def _call_special_tasks_on_hosts(task, hosts):
731 """\
732 Schedules a set of hosts for a special task.
733
734 @returns A list of hostnames that a special task was created for.
735 """
736 models.AclGroup.check_for_acl_violation_hosts(hosts)
737 for host in hosts:
738 models.SpecialTask.schedule_special_task(host, task)
739 return list(sorted(host.hostname for host in hosts))
740
741
showard1ff7b2e2009-05-15 23:17:18 +0000742def reverify_hosts(**filter_data):
743 """\
744 Schedules a set of hosts for verify.
mbligh4e545a52009-12-19 05:30:39 +0000745
746 @returns A list of hostnames that a verify task was created for.
showard1ff7b2e2009-05-15 23:17:18 +0000747 """
Simran Basi73dae552013-02-25 14:57:46 -0800748 return _call_special_tasks_on_hosts(models.SpecialTask.Task.VERIFY,
749 models.Host.query_objects(filter_data))
750
751
752def repair_hosts(**filter_data):
753 """\
754 Schedules a set of hosts for repair.
755
756 @returns A list of hostnames that a repair task was created for.
757 """
758 return _call_special_tasks_on_hosts(models.SpecialTask.Task.REPAIR,
759 models.Host.query_objects(filter_data))
showard1ff7b2e2009-05-15 23:17:18 +0000760
761
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700762def get_jobs(not_yet_run=False, running=False, finished=False,
763 suite=False, sub=False, standalone=False, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000764 """\
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700765 Extra status filter args for get_jobs:
jadmanski0afbb632008-06-06 21:10:57 +0000766 -not_yet_run: Include only jobs that have not yet started running.
767 -running: Include only jobs that have start running but for which not
768 all hosts have completed.
769 -finished: Include only jobs for which all hosts have completed (or
770 aborted).
771 At most one of these three fields should be specified.
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700772
773 Extra type filter args for get_jobs:
774 -suite: Include only jobs with child jobs.
775 -sub: Include only jobs with a parent job.
776 -standalone: Inlcude only jobs with no child or parent jobs.
777 At most one of these three fields should be specified.
jadmanski0afbb632008-06-06 21:10:57 +0000778 """
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700779 extra_args = rpc_utils.extra_job_status_filters(not_yet_run,
780 running,
781 finished)
782 filter_data['extra_args'] = rpc_utils.extra_job_type_filters(extra_args,
783 suite,
784 sub,
785 standalone)
showard0957a842009-05-11 19:25:08 +0000786 job_dicts = []
787 jobs = list(models.Job.query_objects(filter_data))
788 models.Job.objects.populate_relationships(jobs, models.Label,
789 'dependencies')
showardc1a98d12010-01-15 00:22:22 +0000790 models.Job.objects.populate_relationships(jobs, models.JobKeyval, 'keyvals')
showard0957a842009-05-11 19:25:08 +0000791 for job in jobs:
792 job_dict = job.get_object_dict()
793 job_dict['dependencies'] = ','.join(label.name
794 for label in job.dependencies)
showardc1a98d12010-01-15 00:22:22 +0000795 job_dict['keyvals'] = dict((keyval.key, keyval.value)
796 for keyval in job.keyvals)
Eric Lid23bc192011-02-09 14:38:57 -0800797 if job.parameterized_job:
798 job_dict['image'] = get_parameterized_autoupdate_image_url(job)
showard0957a842009-05-11 19:25:08 +0000799 job_dicts.append(job_dict)
800 return rpc_utils.prepare_for_serialization(job_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000801
802
803def get_num_jobs(not_yet_run=False, running=False, finished=False,
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700804 suite=False, sub=False, standalone=False,
jadmanski0afbb632008-06-06 21:10:57 +0000805 **filter_data):
806 """\
807 See get_jobs() for documentation of extra filter parameters.
808 """
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700809 extra_args = rpc_utils.extra_job_status_filters(not_yet_run,
810 running,
811 finished)
812 filter_data['extra_args'] = rpc_utils.extra_job_type_filters(extra_args,
813 suite,
814 sub,
815 standalone)
jadmanski0afbb632008-06-06 21:10:57 +0000816 return models.Job.query_count(filter_data)
mblighe8819cd2008-02-15 16:48:40 +0000817
818
mblighe8819cd2008-02-15 16:48:40 +0000819def get_jobs_summary(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000820 """\
Jiaxi Luoaac54572014-06-04 13:57:02 -0700821 Like get_jobs(), but adds 'status_counts' and 'result_counts' field.
822
823 'status_counts' filed is a dictionary mapping status strings to the number
824 of hosts currently with that status, i.e. {'Queued' : 4, 'Running' : 2}.
825
826 'result_counts' field is piped to tko's rpc_interface and has the return
827 format specified under get_group_counts.
jadmanski0afbb632008-06-06 21:10:57 +0000828 """
829 jobs = get_jobs(**filter_data)
830 ids = [job['id'] for job in jobs]
831 all_status_counts = models.Job.objects.get_status_counts(ids)
832 for job in jobs:
833 job['status_counts'] = all_status_counts[job['id']]
Jiaxi Luoaac54572014-06-04 13:57:02 -0700834 job['result_counts'] = tko_rpc_interface.get_status_counts(
835 ['afe_job_id', 'afe_job_id'],
836 header_groups=[['afe_job_id'], ['afe_job_id']],
837 **{'afe_job_id': job['id']})
jadmanski0afbb632008-06-06 21:10:57 +0000838 return rpc_utils.prepare_for_serialization(jobs)
mblighe8819cd2008-02-15 16:48:40 +0000839
840
showarda965cef2009-05-15 23:17:41 +0000841def get_info_for_clone(id, preserve_metahosts, queue_entry_filter_data=None):
showarda8709c52008-07-03 19:44:54 +0000842 """\
843 Retrieves all the information needed to clone a job.
844 """
showarda8709c52008-07-03 19:44:54 +0000845 job = models.Job.objects.get(id=id)
showard29f7cd22009-04-29 21:16:24 +0000846 job_info = rpc_utils.get_job_info(job,
showarda965cef2009-05-15 23:17:41 +0000847 preserve_metahosts,
848 queue_entry_filter_data)
showard945072f2008-09-03 20:34:59 +0000849
showardd9992fe2008-07-31 02:15:03 +0000850 host_dicts = []
showard29f7cd22009-04-29 21:16:24 +0000851 for host in job_info['hosts']:
852 host_dict = get_hosts(id=host.id)[0]
853 other_labels = host_dict['labels']
854 if host_dict['platform']:
855 other_labels.remove(host_dict['platform'])
856 host_dict['other_labels'] = ', '.join(other_labels)
showardd9992fe2008-07-31 02:15:03 +0000857 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +0000858
showard29f7cd22009-04-29 21:16:24 +0000859 for host in job_info['one_time_hosts']:
860 host_dict = dict(hostname=host.hostname,
861 id=host.id,
862 platform='(one-time host)',
863 locked_text='')
864 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +0000865
showard4d077562009-05-08 18:24:36 +0000866 # convert keys from Label objects to strings (names of labels)
showard29f7cd22009-04-29 21:16:24 +0000867 meta_host_counts = dict((meta_host.name, count) for meta_host, count
showard4d077562009-05-08 18:24:36 +0000868 in job_info['meta_host_counts'].iteritems())
showard29f7cd22009-04-29 21:16:24 +0000869
870 info = dict(job=job.get_object_dict(),
871 meta_host_counts=meta_host_counts,
872 hosts=host_dicts)
873 info['job']['dependencies'] = job_info['dependencies']
874 if job_info['atomic_group']:
875 info['atomic_group_name'] = (job_info['atomic_group']).name
876 else:
877 info['atomic_group_name'] = None
jamesren2275ef12010-04-12 18:25:06 +0000878 info['hostless'] = job_info['hostless']
jamesren76fcf192010-04-21 20:39:50 +0000879 info['drone_set'] = job.drone_set and job.drone_set.name
showarda8709c52008-07-03 19:44:54 +0000880
Eric Lid23bc192011-02-09 14:38:57 -0800881 if job.parameterized_job:
882 info['job']['image'] = get_parameterized_autoupdate_image_url(job)
883
showarda8709c52008-07-03 19:44:54 +0000884 return rpc_utils.prepare_for_serialization(info)
885
886
showard34dc5fa2008-04-24 20:58:40 +0000887# host queue entries
888
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700889def get_host_queue_entries(start_time=None, end_time=None, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000890 """\
showardc92da832009-04-07 18:14:34 +0000891 @returns A sequence of nested dictionaries of host and job information.
jadmanski0afbb632008-06-06 21:10:57 +0000892 """
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700893 filter_data = rpc_utils.inject_times_to_filter('started_on__gte',
894 'started_on__lte',
895 start_time,
896 end_time,
897 **filter_data)
showardc92da832009-04-07 18:14:34 +0000898 return rpc_utils.prepare_rows_as_nested_dicts(
899 models.HostQueueEntry.query_objects(filter_data),
900 ('host', 'atomic_group', 'job'))
showard34dc5fa2008-04-24 20:58:40 +0000901
902
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700903def get_num_host_queue_entries(start_time=None, end_time=None, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000904 """\
905 Get the number of host queue entries associated with this job.
906 """
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700907 filter_data = rpc_utils.inject_times_to_filter('started_on__gte',
908 'started_on__lte',
909 start_time,
910 end_time,
911 **filter_data)
jadmanski0afbb632008-06-06 21:10:57 +0000912 return models.HostQueueEntry.query_count(filter_data)
showard34dc5fa2008-04-24 20:58:40 +0000913
914
showard1e935f12008-07-11 00:11:36 +0000915def get_hqe_percentage_complete(**filter_data):
916 """
showardc92da832009-04-07 18:14:34 +0000917 Computes the fraction of host queue entries matching the given filter data
showard1e935f12008-07-11 00:11:36 +0000918 that are complete.
919 """
920 query = models.HostQueueEntry.query_objects(filter_data)
921 complete_count = query.filter(complete=True).count()
922 total_count = query.count()
923 if total_count == 0:
924 return 1
925 return float(complete_count) / total_count
926
927
showard1a5a4082009-07-28 20:01:37 +0000928# special tasks
929
930def get_special_tasks(**filter_data):
931 return rpc_utils.prepare_rows_as_nested_dicts(
932 models.SpecialTask.query_objects(filter_data),
933 ('host', 'queue_entry'))
934
935
showardc0ac3a72009-07-08 21:14:45 +0000936# support for host detail view
937
Jiaxi Luo79ce6422014-06-13 17:08:09 -0700938def get_host_queue_entries_and_special_tasks(host_id, query_start=None,
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700939 query_limit=None, start_time=None,
940 end_time=None):
showardc0ac3a72009-07-08 21:14:45 +0000941 """
942 @returns an interleaved list of HostQueueEntries and SpecialTasks,
943 in approximate run order. each dict contains keys for type, host,
944 job, status, started_on, execution_path, and ID.
945 """
946 total_limit = None
947 if query_limit is not None:
948 total_limit = query_start + query_limit
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700949 filter_data_common = {'host': host_id,
950 'query_limit': total_limit,
951 'sort_by': ['-id']}
showardc0ac3a72009-07-08 21:14:45 +0000952
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700953 filter_data_queue_entries, filter_data_special_tasks = (
954 rpc_utils.inject_times_to_hqe_special_tasks_filters(
955 filter_data_common, start_time, end_time))
956
957 queue_entries = list(models.HostQueueEntry.query_objects(
958 filter_data_queue_entries))
959 special_tasks = list(models.SpecialTask.query_objects(
960 filter_data_special_tasks))
showardc0ac3a72009-07-08 21:14:45 +0000961
962 interleaved_entries = rpc_utils.interleave_entries(queue_entries,
963 special_tasks)
964 if query_start is not None:
965 interleaved_entries = interleaved_entries[query_start:]
966 if query_limit is not None:
967 interleaved_entries = interleaved_entries[:query_limit]
968 return rpc_utils.prepare_for_serialization(interleaved_entries)
969
970
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700971def get_num_host_queue_entries_and_special_tasks(host_id, start_time=None,
972 end_time=None):
973 filter_data_common = {'host': host_id}
974
975 filter_data_queue_entries, filter_data_special_tasks = (
976 rpc_utils.inject_times_to_hqe_special_tasks_filters(
977 filter_data_common, start_time, end_time))
978
979 return (models.HostQueueEntry.query_count(filter_data_queue_entries)
980 + models.SpecialTask.query_count(filter_data_special_tasks))
showardc0ac3a72009-07-08 21:14:45 +0000981
982
showard29f7cd22009-04-29 21:16:24 +0000983# recurring run
984
985def get_recurring(**filter_data):
986 return rpc_utils.prepare_rows_as_nested_dicts(
987 models.RecurringRun.query_objects(filter_data),
988 ('job', 'owner'))
989
990
991def get_num_recurring(**filter_data):
992 return models.RecurringRun.query_count(filter_data)
993
994
995def delete_recurring_runs(**filter_data):
996 to_delete = models.RecurringRun.query_objects(filter_data)
997 to_delete.delete()
998
999
1000def create_recurring_run(job_id, start_date, loop_period, loop_count):
showard64a95952010-01-13 21:27:16 +00001001 owner = models.User.current_user().login
showard29f7cd22009-04-29 21:16:24 +00001002 job = models.Job.objects.get(id=job_id)
1003 return job.create_recurring_job(start_date=start_date,
1004 loop_period=loop_period,
1005 loop_count=loop_count,
1006 owner=owner)
1007
1008
mblighe8819cd2008-02-15 16:48:40 +00001009# other
1010
showarde0b63622008-08-04 20:58:47 +00001011def echo(data=""):
1012 """\
1013 Returns a passed in string. For doing a basic test to see if RPC calls
1014 can successfully be made.
1015 """
1016 return data
1017
1018
showardb7a52fd2009-04-27 20:10:56 +00001019def get_motd():
1020 """\
1021 Returns the message of the day as a string.
1022 """
1023 return rpc_utils.get_motd()
1024
1025
mblighe8819cd2008-02-15 16:48:40 +00001026def get_static_data():
jadmanski0afbb632008-06-06 21:10:57 +00001027 """\
1028 Returns a dictionary containing a bunch of data that shouldn't change
1029 often and is otherwise inaccessible. This includes:
showardc92da832009-04-07 18:14:34 +00001030
1031 priorities: List of job priority choices.
1032 default_priority: Default priority value for new jobs.
1033 users: Sorted list of all users.
Jiaxi Luo31874592014-06-11 10:36:35 -07001034 labels: Sorted list of labels not start with 'cros-version' and
1035 'fw-version'.
showardc92da832009-04-07 18:14:34 +00001036 atomic_groups: Sorted list of all atomic groups.
1037 tests: Sorted list of all tests.
1038 profilers: Sorted list of all profilers.
1039 current_user: Logged-in username.
1040 host_statuses: Sorted list of possible Host statuses.
1041 job_statuses: Sorted list of possible HostQueueEntry statuses.
Simran Basi7e605742013-11-12 13:43:36 -08001042 job_timeout_default: The default job timeout length in minutes.
showarda1e74b32009-05-12 17:32:04 +00001043 parse_failed_repair_default: Default value for the parse_failed_repair job
Jiaxi Luo31874592014-06-11 10:36:35 -07001044 option.
showardc92da832009-04-07 18:14:34 +00001045 reboot_before_options: A list of valid RebootBefore string enums.
1046 reboot_after_options: A list of valid RebootAfter string enums.
1047 motd: Server's message of the day.
1048 status_dictionary: A mapping from one word job status names to a more
1049 informative description.
jadmanski0afbb632008-06-06 21:10:57 +00001050 """
showard21baa452008-10-21 00:08:39 +00001051
1052 job_fields = models.Job.get_field_dict()
jamesren76fcf192010-04-21 20:39:50 +00001053 default_drone_set_name = models.DroneSet.default_drone_set_name()
1054 drone_sets = ([default_drone_set_name] +
1055 sorted(drone_set.name for drone_set in
1056 models.DroneSet.objects.exclude(
1057 name=default_drone_set_name)))
showard21baa452008-10-21 00:08:39 +00001058
jadmanski0afbb632008-06-06 21:10:57 +00001059 result = {}
Alex Miller7d658cf2013-09-04 16:00:35 -07001060 result['priorities'] = priorities.Priority.choices()
1061 default_priority = priorities.Priority.DEFAULT
1062 result['default_priority'] = 'Default'
1063 result['max_schedulable_priority'] = priorities.Priority.DEFAULT
jadmanski0afbb632008-06-06 21:10:57 +00001064 result['users'] = get_users(sort_by=['login'])
Jiaxi Luo31874592014-06-11 10:36:35 -07001065
1066 label_exclude_filters = [{'name__startswith': 'cros-version'},
1067 {'name__startswith': 'fw-version'}]
1068 result['labels'] = get_labels(
1069 label_exclude_filters,
1070 sort_by=['-platform', 'name'])
1071
showardc92da832009-04-07 18:14:34 +00001072 result['atomic_groups'] = get_atomic_groups(sort_by=['name'])
jadmanski0afbb632008-06-06 21:10:57 +00001073 result['tests'] = get_tests(sort_by=['name'])
showard2b9a88b2008-06-13 20:55:03 +00001074 result['profilers'] = get_profilers(sort_by=['name'])
showard0fc38302008-10-23 00:44:07 +00001075 result['current_user'] = rpc_utils.prepare_for_serialization(
showard64a95952010-01-13 21:27:16 +00001076 models.User.current_user().get_object_dict())
showard2b9a88b2008-06-13 20:55:03 +00001077 result['host_statuses'] = sorted(models.Host.Status.names)
mbligh5a198b92008-12-11 19:33:29 +00001078 result['job_statuses'] = sorted(models.HostQueueEntry.Status.names)
Simran Basi7e605742013-11-12 13:43:36 -08001079 result['job_timeout_mins_default'] = models.Job.DEFAULT_TIMEOUT_MINS
Simran Basi34217022012-11-06 13:43:15 -08001080 result['job_max_runtime_mins_default'] = (
1081 models.Job.DEFAULT_MAX_RUNTIME_MINS)
showarda1e74b32009-05-12 17:32:04 +00001082 result['parse_failed_repair_default'] = bool(
1083 models.Job.DEFAULT_PARSE_FAILED_REPAIR)
jamesrendd855242010-03-02 22:23:44 +00001084 result['reboot_before_options'] = model_attributes.RebootBefore.names
1085 result['reboot_after_options'] = model_attributes.RebootAfter.names
showard8fbae652009-01-20 23:23:10 +00001086 result['motd'] = rpc_utils.get_motd()
jamesren76fcf192010-04-21 20:39:50 +00001087 result['drone_sets_enabled'] = models.DroneSet.drone_sets_enabled()
1088 result['drone_sets'] = drone_sets
jamesren4a41e012010-07-16 22:33:48 +00001089 result['parameterized_jobs'] = models.Job.parameterized_jobs_enabled()
showard8ac29b42008-07-17 17:01:55 +00001090
showardd3dc1992009-04-22 21:01:40 +00001091 result['status_dictionary'] = {"Aborted": "Aborted",
showard8ac29b42008-07-17 17:01:55 +00001092 "Verifying": "Verifying Host",
Alex Millerdfff2fd2013-05-28 13:05:06 -07001093 "Provisioning": "Provisioning Host",
showard8ac29b42008-07-17 17:01:55 +00001094 "Pending": "Waiting on other hosts",
1095 "Running": "Running autoserv",
1096 "Completed": "Autoserv completed",
1097 "Failed": "Failed to complete",
showardd823b362008-07-24 16:35:46 +00001098 "Queued": "Queued",
showard5deb6772008-11-04 21:54:33 +00001099 "Starting": "Next in host's queue",
1100 "Stopped": "Other host(s) failed verify",
showardd3dc1992009-04-22 21:01:40 +00001101 "Parsing": "Awaiting parse of final results",
showard29f7cd22009-04-29 21:16:24 +00001102 "Gathering": "Gathering log files",
showard8cc058f2009-09-08 16:26:33 +00001103 "Template": "Template job for recurring run",
mbligh4608b002010-01-05 18:22:35 +00001104 "Waiting": "Waiting for scheduler action",
Dan Shi07e09af2013-04-12 09:31:29 -07001105 "Archiving": "Archiving results",
1106 "Resetting": "Resetting hosts"}
Jiaxi Luo421608e2014-07-07 14:38:00 -07001107
1108 result['wmatrix_url'] = rpc_utils.get_wmatrix_url()
Simran Basi71206ef2014-08-13 13:51:18 -07001109 result['is_moblab'] = bool(utils.is_moblab())
Jiaxi Luo421608e2014-07-07 14:38:00 -07001110
jadmanski0afbb632008-06-06 21:10:57 +00001111 return result
showard29f7cd22009-04-29 21:16:24 +00001112
1113
1114def get_server_time():
1115 return datetime.datetime.now().strftime("%Y-%m-%d %H:%M")