blob: 76095c4ce0a5cdf007040acffc7adf5047b40a95 [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)
Prashanth Balasubramanian6edaaf92014-11-24 16:36:25 -0800737 shard_host_map = rpc_utils.bucket_hosts_by_shard(hosts)
738 if shard_host_map:
739 raise ValueError('The following hosts are on shards, please '
740 'follow the link to the shards and create jobs '
741 'there instead. %s.' % shard_host_map)
Simran Basi73dae552013-02-25 14:57:46 -0800742 for host in hosts:
743 models.SpecialTask.schedule_special_task(host, task)
744 return list(sorted(host.hostname for host in hosts))
745
746
showard1ff7b2e2009-05-15 23:17:18 +0000747def reverify_hosts(**filter_data):
748 """\
749 Schedules a set of hosts for verify.
mbligh4e545a52009-12-19 05:30:39 +0000750
751 @returns A list of hostnames that a verify task was created for.
showard1ff7b2e2009-05-15 23:17:18 +0000752 """
Simran Basi73dae552013-02-25 14:57:46 -0800753 return _call_special_tasks_on_hosts(models.SpecialTask.Task.VERIFY,
754 models.Host.query_objects(filter_data))
755
756
757def repair_hosts(**filter_data):
758 """\
759 Schedules a set of hosts for repair.
760
761 @returns A list of hostnames that a repair task was created for.
762 """
763 return _call_special_tasks_on_hosts(models.SpecialTask.Task.REPAIR,
764 models.Host.query_objects(filter_data))
showard1ff7b2e2009-05-15 23:17:18 +0000765
766
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700767def get_jobs(not_yet_run=False, running=False, finished=False,
768 suite=False, sub=False, standalone=False, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000769 """\
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700770 Extra status filter args for get_jobs:
jadmanski0afbb632008-06-06 21:10:57 +0000771 -not_yet_run: Include only jobs that have not yet started running.
772 -running: Include only jobs that have start running but for which not
773 all hosts have completed.
774 -finished: Include only jobs for which all hosts have completed (or
775 aborted).
776 At most one of these three fields should be specified.
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700777
778 Extra type filter args for get_jobs:
779 -suite: Include only jobs with child jobs.
780 -sub: Include only jobs with a parent job.
781 -standalone: Inlcude only jobs with no child or parent jobs.
782 At most one of these three fields should be specified.
jadmanski0afbb632008-06-06 21:10:57 +0000783 """
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700784 extra_args = rpc_utils.extra_job_status_filters(not_yet_run,
785 running,
786 finished)
787 filter_data['extra_args'] = rpc_utils.extra_job_type_filters(extra_args,
788 suite,
789 sub,
790 standalone)
showard0957a842009-05-11 19:25:08 +0000791 job_dicts = []
792 jobs = list(models.Job.query_objects(filter_data))
793 models.Job.objects.populate_relationships(jobs, models.Label,
794 'dependencies')
showardc1a98d12010-01-15 00:22:22 +0000795 models.Job.objects.populate_relationships(jobs, models.JobKeyval, 'keyvals')
showard0957a842009-05-11 19:25:08 +0000796 for job in jobs:
797 job_dict = job.get_object_dict()
798 job_dict['dependencies'] = ','.join(label.name
799 for label in job.dependencies)
showardc1a98d12010-01-15 00:22:22 +0000800 job_dict['keyvals'] = dict((keyval.key, keyval.value)
801 for keyval in job.keyvals)
Eric Lid23bc192011-02-09 14:38:57 -0800802 if job.parameterized_job:
803 job_dict['image'] = get_parameterized_autoupdate_image_url(job)
showard0957a842009-05-11 19:25:08 +0000804 job_dicts.append(job_dict)
805 return rpc_utils.prepare_for_serialization(job_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000806
807
808def get_num_jobs(not_yet_run=False, running=False, finished=False,
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700809 suite=False, sub=False, standalone=False,
jadmanski0afbb632008-06-06 21:10:57 +0000810 **filter_data):
811 """\
812 See get_jobs() for documentation of extra filter parameters.
813 """
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700814 extra_args = rpc_utils.extra_job_status_filters(not_yet_run,
815 running,
816 finished)
817 filter_data['extra_args'] = rpc_utils.extra_job_type_filters(extra_args,
818 suite,
819 sub,
820 standalone)
jadmanski0afbb632008-06-06 21:10:57 +0000821 return models.Job.query_count(filter_data)
mblighe8819cd2008-02-15 16:48:40 +0000822
823
mblighe8819cd2008-02-15 16:48:40 +0000824def get_jobs_summary(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000825 """\
Jiaxi Luoaac54572014-06-04 13:57:02 -0700826 Like get_jobs(), but adds 'status_counts' and 'result_counts' field.
827
828 'status_counts' filed is a dictionary mapping status strings to the number
829 of hosts currently with that status, i.e. {'Queued' : 4, 'Running' : 2}.
830
831 'result_counts' field is piped to tko's rpc_interface and has the return
832 format specified under get_group_counts.
jadmanski0afbb632008-06-06 21:10:57 +0000833 """
834 jobs = get_jobs(**filter_data)
835 ids = [job['id'] for job in jobs]
836 all_status_counts = models.Job.objects.get_status_counts(ids)
837 for job in jobs:
838 job['status_counts'] = all_status_counts[job['id']]
Jiaxi Luoaac54572014-06-04 13:57:02 -0700839 job['result_counts'] = tko_rpc_interface.get_status_counts(
840 ['afe_job_id', 'afe_job_id'],
841 header_groups=[['afe_job_id'], ['afe_job_id']],
842 **{'afe_job_id': job['id']})
jadmanski0afbb632008-06-06 21:10:57 +0000843 return rpc_utils.prepare_for_serialization(jobs)
mblighe8819cd2008-02-15 16:48:40 +0000844
845
showarda965cef2009-05-15 23:17:41 +0000846def get_info_for_clone(id, preserve_metahosts, queue_entry_filter_data=None):
showarda8709c52008-07-03 19:44:54 +0000847 """\
848 Retrieves all the information needed to clone a job.
849 """
showarda8709c52008-07-03 19:44:54 +0000850 job = models.Job.objects.get(id=id)
showard29f7cd22009-04-29 21:16:24 +0000851 job_info = rpc_utils.get_job_info(job,
showarda965cef2009-05-15 23:17:41 +0000852 preserve_metahosts,
853 queue_entry_filter_data)
showard945072f2008-09-03 20:34:59 +0000854
showardd9992fe2008-07-31 02:15:03 +0000855 host_dicts = []
showard29f7cd22009-04-29 21:16:24 +0000856 for host in job_info['hosts']:
857 host_dict = get_hosts(id=host.id)[0]
858 other_labels = host_dict['labels']
859 if host_dict['platform']:
860 other_labels.remove(host_dict['platform'])
861 host_dict['other_labels'] = ', '.join(other_labels)
showardd9992fe2008-07-31 02:15:03 +0000862 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +0000863
showard29f7cd22009-04-29 21:16:24 +0000864 for host in job_info['one_time_hosts']:
865 host_dict = dict(hostname=host.hostname,
866 id=host.id,
867 platform='(one-time host)',
868 locked_text='')
869 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +0000870
showard4d077562009-05-08 18:24:36 +0000871 # convert keys from Label objects to strings (names of labels)
showard29f7cd22009-04-29 21:16:24 +0000872 meta_host_counts = dict((meta_host.name, count) for meta_host, count
showard4d077562009-05-08 18:24:36 +0000873 in job_info['meta_host_counts'].iteritems())
showard29f7cd22009-04-29 21:16:24 +0000874
875 info = dict(job=job.get_object_dict(),
876 meta_host_counts=meta_host_counts,
877 hosts=host_dicts)
878 info['job']['dependencies'] = job_info['dependencies']
879 if job_info['atomic_group']:
880 info['atomic_group_name'] = (job_info['atomic_group']).name
881 else:
882 info['atomic_group_name'] = None
jamesren2275ef12010-04-12 18:25:06 +0000883 info['hostless'] = job_info['hostless']
jamesren76fcf192010-04-21 20:39:50 +0000884 info['drone_set'] = job.drone_set and job.drone_set.name
showarda8709c52008-07-03 19:44:54 +0000885
Eric Lid23bc192011-02-09 14:38:57 -0800886 if job.parameterized_job:
887 info['job']['image'] = get_parameterized_autoupdate_image_url(job)
888
showarda8709c52008-07-03 19:44:54 +0000889 return rpc_utils.prepare_for_serialization(info)
890
891
showard34dc5fa2008-04-24 20:58:40 +0000892# host queue entries
893
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700894def get_host_queue_entries(start_time=None, end_time=None, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000895 """\
showardc92da832009-04-07 18:14:34 +0000896 @returns A sequence of nested dictionaries of host and job information.
jadmanski0afbb632008-06-06 21:10:57 +0000897 """
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700898 filter_data = rpc_utils.inject_times_to_filter('started_on__gte',
899 'started_on__lte',
900 start_time,
901 end_time,
902 **filter_data)
showardc92da832009-04-07 18:14:34 +0000903 return rpc_utils.prepare_rows_as_nested_dicts(
904 models.HostQueueEntry.query_objects(filter_data),
905 ('host', 'atomic_group', 'job'))
showard34dc5fa2008-04-24 20:58:40 +0000906
907
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700908def get_num_host_queue_entries(start_time=None, end_time=None, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000909 """\
910 Get the number of host queue entries associated with this job.
911 """
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700912 filter_data = rpc_utils.inject_times_to_filter('started_on__gte',
913 'started_on__lte',
914 start_time,
915 end_time,
916 **filter_data)
jadmanski0afbb632008-06-06 21:10:57 +0000917 return models.HostQueueEntry.query_count(filter_data)
showard34dc5fa2008-04-24 20:58:40 +0000918
919
showard1e935f12008-07-11 00:11:36 +0000920def get_hqe_percentage_complete(**filter_data):
921 """
showardc92da832009-04-07 18:14:34 +0000922 Computes the fraction of host queue entries matching the given filter data
showard1e935f12008-07-11 00:11:36 +0000923 that are complete.
924 """
925 query = models.HostQueueEntry.query_objects(filter_data)
926 complete_count = query.filter(complete=True).count()
927 total_count = query.count()
928 if total_count == 0:
929 return 1
930 return float(complete_count) / total_count
931
932
showard1a5a4082009-07-28 20:01:37 +0000933# special tasks
934
935def get_special_tasks(**filter_data):
936 return rpc_utils.prepare_rows_as_nested_dicts(
937 models.SpecialTask.query_objects(filter_data),
938 ('host', 'queue_entry'))
939
940
showardc0ac3a72009-07-08 21:14:45 +0000941# support for host detail view
942
Jiaxi Luo79ce6422014-06-13 17:08:09 -0700943def get_host_queue_entries_and_special_tasks(host_id, query_start=None,
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700944 query_limit=None, start_time=None,
945 end_time=None):
showardc0ac3a72009-07-08 21:14:45 +0000946 """
947 @returns an interleaved list of HostQueueEntries and SpecialTasks,
948 in approximate run order. each dict contains keys for type, host,
949 job, status, started_on, execution_path, and ID.
950 """
951 total_limit = None
952 if query_limit is not None:
953 total_limit = query_start + query_limit
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700954 filter_data_common = {'host': host_id,
955 'query_limit': total_limit,
956 'sort_by': ['-id']}
showardc0ac3a72009-07-08 21:14:45 +0000957
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700958 filter_data_queue_entries, filter_data_special_tasks = (
959 rpc_utils.inject_times_to_hqe_special_tasks_filters(
960 filter_data_common, start_time, end_time))
961
962 queue_entries = list(models.HostQueueEntry.query_objects(
963 filter_data_queue_entries))
964 special_tasks = list(models.SpecialTask.query_objects(
965 filter_data_special_tasks))
showardc0ac3a72009-07-08 21:14:45 +0000966
967 interleaved_entries = rpc_utils.interleave_entries(queue_entries,
968 special_tasks)
969 if query_start is not None:
970 interleaved_entries = interleaved_entries[query_start:]
971 if query_limit is not None:
972 interleaved_entries = interleaved_entries[:query_limit]
973 return rpc_utils.prepare_for_serialization(interleaved_entries)
974
975
Jiaxi Luo57bc1952014-07-22 15:27:30 -0700976def get_num_host_queue_entries_and_special_tasks(host_id, start_time=None,
977 end_time=None):
978 filter_data_common = {'host': host_id}
979
980 filter_data_queue_entries, filter_data_special_tasks = (
981 rpc_utils.inject_times_to_hqe_special_tasks_filters(
982 filter_data_common, start_time, end_time))
983
984 return (models.HostQueueEntry.query_count(filter_data_queue_entries)
985 + models.SpecialTask.query_count(filter_data_special_tasks))
showardc0ac3a72009-07-08 21:14:45 +0000986
987
showard29f7cd22009-04-29 21:16:24 +0000988# recurring run
989
990def get_recurring(**filter_data):
991 return rpc_utils.prepare_rows_as_nested_dicts(
992 models.RecurringRun.query_objects(filter_data),
993 ('job', 'owner'))
994
995
996def get_num_recurring(**filter_data):
997 return models.RecurringRun.query_count(filter_data)
998
999
1000def delete_recurring_runs(**filter_data):
1001 to_delete = models.RecurringRun.query_objects(filter_data)
1002 to_delete.delete()
1003
1004
1005def create_recurring_run(job_id, start_date, loop_period, loop_count):
showard64a95952010-01-13 21:27:16 +00001006 owner = models.User.current_user().login
showard29f7cd22009-04-29 21:16:24 +00001007 job = models.Job.objects.get(id=job_id)
1008 return job.create_recurring_job(start_date=start_date,
1009 loop_period=loop_period,
1010 loop_count=loop_count,
1011 owner=owner)
1012
1013
mblighe8819cd2008-02-15 16:48:40 +00001014# other
1015
showarde0b63622008-08-04 20:58:47 +00001016def echo(data=""):
1017 """\
1018 Returns a passed in string. For doing a basic test to see if RPC calls
1019 can successfully be made.
1020 """
1021 return data
1022
1023
showardb7a52fd2009-04-27 20:10:56 +00001024def get_motd():
1025 """\
1026 Returns the message of the day as a string.
1027 """
1028 return rpc_utils.get_motd()
1029
1030
mblighe8819cd2008-02-15 16:48:40 +00001031def get_static_data():
jadmanski0afbb632008-06-06 21:10:57 +00001032 """\
1033 Returns a dictionary containing a bunch of data that shouldn't change
1034 often and is otherwise inaccessible. This includes:
showardc92da832009-04-07 18:14:34 +00001035
1036 priorities: List of job priority choices.
1037 default_priority: Default priority value for new jobs.
1038 users: Sorted list of all users.
Jiaxi Luo31874592014-06-11 10:36:35 -07001039 labels: Sorted list of labels not start with 'cros-version' and
1040 'fw-version'.
showardc92da832009-04-07 18:14:34 +00001041 atomic_groups: Sorted list of all atomic groups.
1042 tests: Sorted list of all tests.
1043 profilers: Sorted list of all profilers.
1044 current_user: Logged-in username.
1045 host_statuses: Sorted list of possible Host statuses.
1046 job_statuses: Sorted list of possible HostQueueEntry statuses.
Simran Basi7e605742013-11-12 13:43:36 -08001047 job_timeout_default: The default job timeout length in minutes.
showarda1e74b32009-05-12 17:32:04 +00001048 parse_failed_repair_default: Default value for the parse_failed_repair job
Jiaxi Luo31874592014-06-11 10:36:35 -07001049 option.
showardc92da832009-04-07 18:14:34 +00001050 reboot_before_options: A list of valid RebootBefore string enums.
1051 reboot_after_options: A list of valid RebootAfter string enums.
1052 motd: Server's message of the day.
1053 status_dictionary: A mapping from one word job status names to a more
1054 informative description.
jadmanski0afbb632008-06-06 21:10:57 +00001055 """
showard21baa452008-10-21 00:08:39 +00001056
1057 job_fields = models.Job.get_field_dict()
jamesren76fcf192010-04-21 20:39:50 +00001058 default_drone_set_name = models.DroneSet.default_drone_set_name()
1059 drone_sets = ([default_drone_set_name] +
1060 sorted(drone_set.name for drone_set in
1061 models.DroneSet.objects.exclude(
1062 name=default_drone_set_name)))
showard21baa452008-10-21 00:08:39 +00001063
jadmanski0afbb632008-06-06 21:10:57 +00001064 result = {}
Alex Miller7d658cf2013-09-04 16:00:35 -07001065 result['priorities'] = priorities.Priority.choices()
1066 default_priority = priorities.Priority.DEFAULT
1067 result['default_priority'] = 'Default'
1068 result['max_schedulable_priority'] = priorities.Priority.DEFAULT
jadmanski0afbb632008-06-06 21:10:57 +00001069 result['users'] = get_users(sort_by=['login'])
Jiaxi Luo31874592014-06-11 10:36:35 -07001070
1071 label_exclude_filters = [{'name__startswith': 'cros-version'},
1072 {'name__startswith': 'fw-version'}]
1073 result['labels'] = get_labels(
1074 label_exclude_filters,
1075 sort_by=['-platform', 'name'])
1076
showardc92da832009-04-07 18:14:34 +00001077 result['atomic_groups'] = get_atomic_groups(sort_by=['name'])
jadmanski0afbb632008-06-06 21:10:57 +00001078 result['tests'] = get_tests(sort_by=['name'])
showard2b9a88b2008-06-13 20:55:03 +00001079 result['profilers'] = get_profilers(sort_by=['name'])
showard0fc38302008-10-23 00:44:07 +00001080 result['current_user'] = rpc_utils.prepare_for_serialization(
showard64a95952010-01-13 21:27:16 +00001081 models.User.current_user().get_object_dict())
showard2b9a88b2008-06-13 20:55:03 +00001082 result['host_statuses'] = sorted(models.Host.Status.names)
mbligh5a198b92008-12-11 19:33:29 +00001083 result['job_statuses'] = sorted(models.HostQueueEntry.Status.names)
Simran Basi7e605742013-11-12 13:43:36 -08001084 result['job_timeout_mins_default'] = models.Job.DEFAULT_TIMEOUT_MINS
Simran Basi34217022012-11-06 13:43:15 -08001085 result['job_max_runtime_mins_default'] = (
1086 models.Job.DEFAULT_MAX_RUNTIME_MINS)
showarda1e74b32009-05-12 17:32:04 +00001087 result['parse_failed_repair_default'] = bool(
1088 models.Job.DEFAULT_PARSE_FAILED_REPAIR)
jamesrendd855242010-03-02 22:23:44 +00001089 result['reboot_before_options'] = model_attributes.RebootBefore.names
1090 result['reboot_after_options'] = model_attributes.RebootAfter.names
showard8fbae652009-01-20 23:23:10 +00001091 result['motd'] = rpc_utils.get_motd()
jamesren76fcf192010-04-21 20:39:50 +00001092 result['drone_sets_enabled'] = models.DroneSet.drone_sets_enabled()
1093 result['drone_sets'] = drone_sets
jamesren4a41e012010-07-16 22:33:48 +00001094 result['parameterized_jobs'] = models.Job.parameterized_jobs_enabled()
showard8ac29b42008-07-17 17:01:55 +00001095
showardd3dc1992009-04-22 21:01:40 +00001096 result['status_dictionary'] = {"Aborted": "Aborted",
showard8ac29b42008-07-17 17:01:55 +00001097 "Verifying": "Verifying Host",
Alex Millerdfff2fd2013-05-28 13:05:06 -07001098 "Provisioning": "Provisioning Host",
showard8ac29b42008-07-17 17:01:55 +00001099 "Pending": "Waiting on other hosts",
1100 "Running": "Running autoserv",
1101 "Completed": "Autoserv completed",
1102 "Failed": "Failed to complete",
showardd823b362008-07-24 16:35:46 +00001103 "Queued": "Queued",
showard5deb6772008-11-04 21:54:33 +00001104 "Starting": "Next in host's queue",
1105 "Stopped": "Other host(s) failed verify",
showardd3dc1992009-04-22 21:01:40 +00001106 "Parsing": "Awaiting parse of final results",
showard29f7cd22009-04-29 21:16:24 +00001107 "Gathering": "Gathering log files",
showard8cc058f2009-09-08 16:26:33 +00001108 "Template": "Template job for recurring run",
mbligh4608b002010-01-05 18:22:35 +00001109 "Waiting": "Waiting for scheduler action",
Dan Shi07e09af2013-04-12 09:31:29 -07001110 "Archiving": "Archiving results",
1111 "Resetting": "Resetting hosts"}
Jiaxi Luo421608e2014-07-07 14:38:00 -07001112
1113 result['wmatrix_url'] = rpc_utils.get_wmatrix_url()
Simran Basi71206ef2014-08-13 13:51:18 -07001114 result['is_moblab'] = bool(utils.is_moblab())
Jiaxi Luo421608e2014-07-07 14:38:00 -07001115
jadmanski0afbb632008-06-06 21:10:57 +00001116 return result
showard29f7cd22009-04-29 21:16:24 +00001117
1118
1119def get_server_time():
1120 return datetime.datetime.now().strftime("%Y-%m-%d %H:%M")