blob: d9fe28120114e85eca07b24c9166499d9ec0b1cc [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
MK Ryu9c5fbbe2015-02-11 15:46:22 -080034import sys
showard29f7cd22009-04-29 21:16:24 +000035import datetime
MK Ryu9c5fbbe2015-02-11 15:46:22 -080036
Moises Osorio2dc7a102014-12-02 18:24:02 -080037from django.db.models import Count
showardcafd16e2009-05-29 18:37:49 +000038import common
Simran Basib6ec8ae2014-04-23 12:05:08 -070039from autotest_lib.client.common_lib import priorities
Gabe Black1e1c41b2015-02-04 23:55:15 -080040from autotest_lib.client.common_lib.cros.graphite import autotest_stats
showard6d7b2ff2009-06-10 00:16:47 +000041from autotest_lib.frontend.afe import control_file, rpc_utils
J. Richard Barnetteb5164d62015-04-13 12:59:31 -070042from autotest_lib.frontend.afe import models, model_logic, model_attributes
Simran Basib6ec8ae2014-04-23 12:05:08 -070043from autotest_lib.frontend.afe import site_rpc_interface
Moises Osorio2dc7a102014-12-02 18:24:02 -080044from autotest_lib.frontend.tko import models as tko_models
Jiaxi Luoaac54572014-06-04 13:57:02 -070045from autotest_lib.frontend.tko import rpc_interface as tko_rpc_interface
J. Richard Barnetteb5164d62015-04-13 12:59:31 -070046from autotest_lib.server import frontend
Simran Basi71206ef2014-08-13 13:51:18 -070047from autotest_lib.server import utils
Jiaxi Luo90190c92014-06-18 12:35:57 -070048from autotest_lib.server.cros.dynamic_suite import tools
J. Richard Barnette39255fa2015-04-14 17:23:41 -070049from autotest_lib.site_utils import status_history
mblighe8819cd2008-02-15 16:48:40 +000050
Moises Osorio2dc7a102014-12-02 18:24:02 -080051
Gabe Black1e1c41b2015-02-04 23:55:15 -080052_timer = autotest_stats.Timer('rpc_interface')
Moises Osorio2dc7a102014-12-02 18:24:02 -080053
Eric Lid23bc192011-02-09 14:38:57 -080054def get_parameterized_autoupdate_image_url(job):
55 """Get the parameterized autoupdate image url from a parameterized job."""
56 known_test_obj = models.Test.smart_get('autoupdate_ParameterizedJob')
57 image_parameter = known_test_obj.testparameter_set.get(test=known_test_obj,
beeps8bb1f7d2013-08-05 01:30:09 -070058 name='image')
Eric Lid23bc192011-02-09 14:38:57 -080059 para_set = job.parameterized_job.parameterizedjobparameter_set
60 job_test_para = para_set.get(test_parameter=image_parameter)
61 return job_test_para.parameter_value
62
63
mblighe8819cd2008-02-15 16:48:40 +000064# labels
65
mblighe8819cd2008-02-15 16:48:40 +000066def modify_label(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +000067 models.Label.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +000068
69
70def delete_label(id):
jadmanski0afbb632008-06-06 21:10:57 +000071 models.Label.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +000072
Prashanth Balasubramanian744898f2015-01-13 05:04:16 -080073
MK Ryu9c5fbbe2015-02-11 15:46:22 -080074def add_label(name, ignore_exception_if_exists=False, **kwargs):
MK Ryucf027c62015-03-04 12:00:50 -080075 """Adds a new label of a given name.
MK Ryu9c5fbbe2015-02-11 15:46:22 -080076
77 @param name: label name.
78 @param ignore_exception_if_exists: If True and the exception was
79 thrown due to the duplicated label name when adding a label,
80 then suppress the exception. Default is False.
81 @param kwargs: keyword args that store more info about a label
82 other than the name.
83 @return: int/long id of a new label.
84 """
85 # models.Label.add_object() throws model_logic.ValidationError
86 # when it is given a label name that already exists.
87 # However, ValidationError can be thrown with different errors,
88 # and those errors should be thrown up to the call chain.
89 try:
90 label = models.Label.add_object(name=name, **kwargs)
91 except:
92 exc_info = sys.exc_info()
93 if ignore_exception_if_exists:
94 label = rpc_utils.get_label(name)
95 # If the exception is raised not because of duplicated
96 # "name", then raise the original exception.
97 if label is None:
98 raise exc_info[0], exc_info[1], exc_info[2]
99 else:
100 raise exc_info[0], exc_info[1], exc_info[2]
101 return label.id
102
103
104def add_label_to_hosts(id, hosts):
MK Ryucf027c62015-03-04 12:00:50 -0800105 """Adds a label of the given id to the given hosts only in local DB.
MK Ryu9c5fbbe2015-02-11 15:46:22 -0800106
107 @param id: id or name of a label. More often a label name.
108 @param hosts: The hostnames of hosts that need the label.
109
110 @raises models.Label.DoesNotExist: If the label with id doesn't exist.
111 """
112 label = models.Label.smart_get(id)
113 host_objs = models.Host.smart_get_bulk(hosts)
114 if label.platform:
115 models.Host.check_no_platform(host_objs)
116 label.host_set.add(*host_objs)
117
118
119def label_add_hosts(id, hosts):
MK Ryucf027c62015-03-04 12:00:50 -0800120 """Adds a label with the given id to the given hosts.
MK Ryu9c5fbbe2015-02-11 15:46:22 -0800121
122 This method should be run only on master not shards.
Prashanth Balasubramanian5949b4a2014-11-23 12:58:30 -0800123 The given label will be created if it doesn't exist, provided the `id`
124 supplied is a label name not an int/long id.
125
MK Ryu9c5fbbe2015-02-11 15:46:22 -0800126 @param id: id or name of a label. More often a label name.
Prashanth Balasubramanian5949b4a2014-11-23 12:58:30 -0800127 @param hosts: A list of hostnames or ids. More often hostnames.
128
MK Ryu9c5fbbe2015-02-11 15:46:22 -0800129 @raises ValueError: If the id specified is an int/long (label id)
130 while the label does not exist.
Prashanth Balasubramanian5949b4a2014-11-23 12:58:30 -0800131 """
MK Ryu9c5fbbe2015-02-11 15:46:22 -0800132 # This RPC call should be accepted only by master.
133 if utils.is_shard():
134 rpc_utils.route_rpc_to_master('label_add_hosts', id=id, hosts=hosts)
135 return
136
Prashanth Balasubramanian5949b4a2014-11-23 12:58:30 -0800137 try:
Prashanth Balasubramanian5949b4a2014-11-23 12:58:30 -0800138 label = models.Label.smart_get(id)
139 except models.Label.DoesNotExist:
140 # This matches the type checks in smart_get, which is a hack
141 # in and off itself. The aim here is to create any non-existent
142 # label, which we cannot do if the 'id' specified isn't a label name.
143 if isinstance(id, basestring):
144 label = models.Label.smart_get(add_label(id))
145 else:
MK Ryu9c5fbbe2015-02-11 15:46:22 -0800146 raise ValueError('Label id (%s) does not exist. Please specify '
147 'the argument, id, as a string (label name).'
148 % id)
149 add_label_to_hosts(id, hosts)
MK Ryucf027c62015-03-04 12:00:50 -0800150
151 host_objs = models.Host.smart_get_bulk(hosts)
MK Ryu9c5fbbe2015-02-11 15:46:22 -0800152 # Make sure the label exists on the shard with the same id
153 # as it is on the master.
MK Ryucf027c62015-03-04 12:00:50 -0800154 # It is possible that the label is already in a shard because
155 # we are adding a new label only to shards of hosts that the label
156 # is going to be attached.
157 # For example, we add a label L1 to a host in shard S1.
158 # Master and S1 will have L1 but other shards won't.
159 # Later, when we add the same label L1 to hosts in shards S1 and S2,
160 # S1 already has the label but S2 doesn't.
161 # S2 should have the new label without any problem.
MK Ryu9c5fbbe2015-02-11 15:46:22 -0800162 # We ignore exception in such a case.
163 rpc_utils.fanout_rpc(
164 host_objs, 'add_label', name=label.name, id=label.id,
165 include_hostnames=False, ignore_exception_if_exists=True)
166 rpc_utils.fanout_rpc(host_objs, 'add_label_to_hosts', id=id)
showardbbabf502008-06-06 00:02:02 +0000167
168
MK Ryucf027c62015-03-04 12:00:50 -0800169def remove_label_from_hosts(id, hosts):
170 """Removes a label of the given id from the given hosts only in local DB.
171
172 @param id: id or name of a label.
173 @param hosts: The hostnames of hosts that need to remove the label from.
174 """
showardbe3ec042008-11-12 18:16:07 +0000175 host_objs = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000176 models.Label.smart_get(id).host_set.remove(*host_objs)
showardbbabf502008-06-06 00:02:02 +0000177
178
MK Ryucf027c62015-03-04 12:00:50 -0800179def label_remove_hosts(id, hosts):
180 """Removes a label of the given id from the given hosts.
181
182 This method should be run only on master not shards.
183
184 @param id: id or name of a label.
185 @param hosts: A list of hostnames or ids. More often hostnames.
186 """
187 # This RPC call should be accepted only by master.
188 if utils.is_shard():
189 rpc_utils.route_rpc_to_master('label_remove_hosts', id=id, hosts=hosts)
190 return
191
192 remove_label_from_hosts(id, hosts)
193 host_objs = models.Host.smart_get_bulk(hosts)
194 rpc_utils.fanout_rpc(host_objs, 'remove_label_from_hosts', id=id)
195
196
Jiaxi Luo31874592014-06-11 10:36:35 -0700197def get_labels(exclude_filters=(), **filter_data):
showardc92da832009-04-07 18:14:34 +0000198 """\
Jiaxi Luo31874592014-06-11 10:36:35 -0700199 @param exclude_filters: A sequence of dictionaries of filters.
200
showardc92da832009-04-07 18:14:34 +0000201 @returns A sequence of nested dictionaries of label information.
202 """
Jiaxi Luo31874592014-06-11 10:36:35 -0700203 labels = models.Label.query_objects(filter_data)
204 for exclude_filter in exclude_filters:
205 labels = labels.exclude(**exclude_filter)
206 return rpc_utils.prepare_rows_as_nested_dicts(labels, ('atomic_group',))
showardc92da832009-04-07 18:14:34 +0000207
208
209# atomic groups
210
showarde9450c92009-06-30 01:58:52 +0000211def add_atomic_group(name, max_number_of_machines=None, description=None):
showardc92da832009-04-07 18:14:34 +0000212 return models.AtomicGroup.add_object(
213 name=name, max_number_of_machines=max_number_of_machines,
214 description=description).id
215
216
217def modify_atomic_group(id, **data):
218 models.AtomicGroup.smart_get(id).update_object(data)
219
220
221def delete_atomic_group(id):
222 models.AtomicGroup.smart_get(id).delete()
223
224
225def atomic_group_add_labels(id, labels):
226 label_objs = models.Label.smart_get_bulk(labels)
227 models.AtomicGroup.smart_get(id).label_set.add(*label_objs)
228
229
230def atomic_group_remove_labels(id, labels):
231 label_objs = models.Label.smart_get_bulk(labels)
232 models.AtomicGroup.smart_get(id).label_set.remove(*label_objs)
233
234
235def get_atomic_groups(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000236 return rpc_utils.prepare_for_serialization(
showardc92da832009-04-07 18:14:34 +0000237 models.AtomicGroup.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000238
239
240# hosts
241
Matthew Sartori68186332015-04-27 17:19:53 -0700242def add_host(hostname, status=None, locked=None, lock_reason='', protection=None):
243 if locked and not lock_reason:
244 raise model_logic.ValidationError(
245 {'locked': 'Please provide a reason for locking when adding host.'})
246
jadmanski0afbb632008-06-06 21:10:57 +0000247 return models.Host.add_object(hostname=hostname, status=status,
Matthew Sartori68186332015-04-27 17:19:53 -0700248 locked=locked, lock_reason=lock_reason,
249 protection=protection).id
mblighe8819cd2008-02-15 16:48:40 +0000250
251
Jakob Juelich50e91f72014-10-01 12:43:23 -0700252@rpc_utils.forward_single_host_rpc_to_shard
mblighe8819cd2008-02-15 16:48:40 +0000253def modify_host(id, **data):
Jakob Juelich50e91f72014-10-01 12:43:23 -0700254 """Modify local attributes of a host.
255
256 If this is called on the master, but the host is assigned to a shard, this
257 will also forward the call to the responsible shard. This means i.e. if a
258 host is being locked using this function, this change will also propagate to
259 shards.
260
261 @param id: id of the host to modify.
262 @param **data: key=value pairs of values to set on the host.
263 """
showardbe0d8692009-08-20 23:42:44 +0000264 rpc_utils.check_modify_host(data)
showardce7c0922009-09-11 18:39:24 +0000265 host = models.Host.smart_get(id)
Jakob Juelich50e91f72014-10-01 12:43:23 -0700266
showardce7c0922009-09-11 18:39:24 +0000267 rpc_utils.check_modify_host_locking(host, data)
268 host.update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000269
270
showard276f9442009-05-20 00:33:16 +0000271def modify_hosts(host_filter_data, update_data):
Jakob Juelich50e91f72014-10-01 12:43:23 -0700272 """Modify local attributes of multiple hosts.
273
274 If this is called on the master, but one of the hosts in that match the
275 filters is assigned to a shard, this will also forward the call to the
276 responsible shard.
277
278 The filters are always applied on the master, not on the shards. This means
279 if the states of a host differ on the master and a shard, the state on the
280 master will be used. I.e. this means:
281 A host was synced to Shard 1. On Shard 1 the status of the host was set to
282 'Repair Failed'.
283 - A call to modify_hosts with host_filter_data={'status': 'Ready'} will
284 update the host (both on the shard and on the master), because the state
285 of the host as the master knows it is still 'Ready'.
286 - A call to modify_hosts with host_filter_data={'status': 'Repair failed'
287 will not update the host, because the filter doesn't apply on the master.
288
showardbe0d8692009-08-20 23:42:44 +0000289 @param host_filter_data: Filters out which hosts to modify.
290 @param update_data: A dictionary with the changes to make to the hosts.
showard276f9442009-05-20 00:33:16 +0000291 """
showardbe0d8692009-08-20 23:42:44 +0000292 rpc_utils.check_modify_host(update_data)
showard276f9442009-05-20 00:33:16 +0000293 hosts = models.Host.query_objects(host_filter_data)
Jakob Juelich50e91f72014-10-01 12:43:23 -0700294
295 affected_shard_hostnames = set()
296 affected_host_ids = []
297
Alex Miller9658a952013-05-14 16:40:02 -0700298 # Check all hosts before changing data for exception safety.
299 for host in hosts:
300 rpc_utils.check_modify_host_locking(host, update_data)
Jakob Juelich50e91f72014-10-01 12:43:23 -0700301 if host.shard:
Prashanth Balasubramanian8c98ac12014-12-23 11:26:44 -0800302 affected_shard_hostnames.add(host.shard.rpc_hostname())
Jakob Juelich50e91f72014-10-01 12:43:23 -0700303 affected_host_ids.append(host.id)
304
Prashanth Balasubramanian8c98ac12014-12-23 11:26:44 -0800305 if not utils.is_shard():
Jakob Juelich50e91f72014-10-01 12:43:23 -0700306 # Caution: Changing the filter from the original here. See docstring.
307 rpc_utils.run_rpc_on_multiple_hostnames(
308 'modify_hosts', affected_shard_hostnames,
309 host_filter_data={'id__in': affected_host_ids},
310 update_data=update_data)
311
showard276f9442009-05-20 00:33:16 +0000312 for host in hosts:
313 host.update_object(update_data)
314
315
mblighe8819cd2008-02-15 16:48:40 +0000316def host_add_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000317 labels = models.Label.smart_get_bulk(labels)
showardcafd16e2009-05-29 18:37:49 +0000318 host = models.Host.smart_get(id)
319
320 platforms = [label.name for label in labels if label.platform]
321 if len(platforms) > 1:
322 raise model_logic.ValidationError(
323 {'labels': 'Adding more than one platform label: %s' %
324 ', '.join(platforms)})
325 if len(platforms) == 1:
326 models.Host.check_no_platform([host])
327 host.labels.add(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000328
329
330def host_remove_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000331 labels = models.Label.smart_get_bulk(labels)
jadmanski0afbb632008-06-06 21:10:57 +0000332 models.Host.smart_get(id).labels.remove(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000333
334
MK Ryuacf35922014-10-03 14:56:49 -0700335def get_host_attribute(attribute, **host_filter_data):
336 """
337 @param attribute: string name of attribute
338 @param host_filter_data: filter data to apply to Hosts to choose hosts to
339 act upon
340 """
341 hosts = rpc_utils.get_host_query((), False, False, True, host_filter_data)
342 hosts = list(hosts)
343 models.Host.objects.populate_relationships(hosts, models.HostAttribute,
344 'attribute_list')
345 host_attr_dicts = []
346 for host_obj in hosts:
347 for attr_obj in host_obj.attribute_list:
348 if attr_obj.attribute == attribute:
349 host_attr_dicts.append(attr_obj.get_object_dict())
350 return rpc_utils.prepare_for_serialization(host_attr_dicts)
351
352
showard0957a842009-05-11 19:25:08 +0000353def set_host_attribute(attribute, value, **host_filter_data):
354 """
355 @param attribute string name of attribute
356 @param value string, or None to delete an attribute
357 @param host_filter_data filter data to apply to Hosts to choose hosts to act
358 upon
359 """
360 assert host_filter_data # disallow accidental actions on all hosts
361 hosts = models.Host.query_objects(host_filter_data)
362 models.AclGroup.check_for_acl_violation_hosts(hosts)
363
364 for host in hosts:
showardf8b19042009-05-12 17:22:49 +0000365 host.set_or_delete_attribute(attribute, value)
showard0957a842009-05-11 19:25:08 +0000366
367
Jakob Juelich50e91f72014-10-01 12:43:23 -0700368@rpc_utils.forward_single_host_rpc_to_shard
mblighe8819cd2008-02-15 16:48:40 +0000369def delete_host(id):
jadmanski0afbb632008-06-06 21:10:57 +0000370 models.Host.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000371
372
showard87cc38f2009-08-20 23:37:04 +0000373def get_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000374 exclude_atomic_group_hosts=False, valid_only=True, **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000375 """
376 @param multiple_labels: match hosts in all of the labels given. Should
377 be a list of label names.
378 @param exclude_only_if_needed_labels: Exclude hosts with at least one
379 "only_if_needed" label applied.
380 @param exclude_atomic_group_hosts: Exclude hosts that have one or more
381 atomic group labels associated with them.
jadmanski0afbb632008-06-06 21:10:57 +0000382 """
showard43a3d262008-11-12 18:17:05 +0000383 hosts = rpc_utils.get_host_query(multiple_labels,
384 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000385 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000386 valid_only, filter_data)
showard0957a842009-05-11 19:25:08 +0000387 hosts = list(hosts)
388 models.Host.objects.populate_relationships(hosts, models.Label,
389 'label_list')
390 models.Host.objects.populate_relationships(hosts, models.AclGroup,
391 'acl_list')
392 models.Host.objects.populate_relationships(hosts, models.HostAttribute,
393 'attribute_list')
showard43a3d262008-11-12 18:17:05 +0000394 host_dicts = []
395 for host_obj in hosts:
396 host_dict = host_obj.get_object_dict()
showard0957a842009-05-11 19:25:08 +0000397 host_dict['labels'] = [label.name for label in host_obj.label_list]
showard909c9142009-07-07 20:54:42 +0000398 host_dict['platform'], host_dict['atomic_group'] = (rpc_utils.
399 find_platform_and_atomic_group(host_obj))
showard0957a842009-05-11 19:25:08 +0000400 host_dict['acls'] = [acl.name for acl in host_obj.acl_list]
401 host_dict['attributes'] = dict((attribute.attribute, attribute.value)
402 for attribute in host_obj.attribute_list)
showard43a3d262008-11-12 18:17:05 +0000403 host_dicts.append(host_dict)
404 return rpc_utils.prepare_for_serialization(host_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000405
406
showard87cc38f2009-08-20 23:37:04 +0000407def get_num_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000408 exclude_atomic_group_hosts=False, valid_only=True,
409 **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000410 """
411 Same parameters as get_hosts().
412
413 @returns The number of matching hosts.
414 """
showard43a3d262008-11-12 18:17:05 +0000415 hosts = rpc_utils.get_host_query(multiple_labels,
416 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000417 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000418 valid_only, filter_data)
showard43a3d262008-11-12 18:17:05 +0000419 return hosts.count()
showard1385b162008-03-13 15:59:40 +0000420
mblighe8819cd2008-02-15 16:48:40 +0000421
422# tests
423
showard909c7a62008-07-15 21:52:38 +0000424def add_test(name, test_type, path, author=None, dependencies=None,
showard3d9899a2008-07-31 02:11:58 +0000425 experimental=True, run_verify=None, test_class=None,
showard909c7a62008-07-15 21:52:38 +0000426 test_time=None, test_category=None, description=None,
427 sync_count=1):
jadmanski0afbb632008-06-06 21:10:57 +0000428 return models.Test.add_object(name=name, test_type=test_type, path=path,
showard909c7a62008-07-15 21:52:38 +0000429 author=author, dependencies=dependencies,
430 experimental=experimental,
431 run_verify=run_verify, test_time=test_time,
432 test_category=test_category,
433 sync_count=sync_count,
jadmanski0afbb632008-06-06 21:10:57 +0000434 test_class=test_class,
435 description=description).id
mblighe8819cd2008-02-15 16:48:40 +0000436
437
438def modify_test(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000439 models.Test.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000440
441
442def delete_test(id):
jadmanski0afbb632008-06-06 21:10:57 +0000443 models.Test.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000444
445
446def get_tests(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000447 return rpc_utils.prepare_for_serialization(
448 models.Test.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000449
450
Moises Osorio2dc7a102014-12-02 18:24:02 -0800451@_timer.decorate
452def get_tests_status_counts_by_job_name_label(job_name_prefix, label_name):
453 """Gets the counts of all passed and failed tests from the matching jobs.
454
455 @param job_name_prefix: Name prefix of the jobs to get the summary from, e.g.,
456 'butterfly-release/R40-6457.21.0/bvt-cq/'.
457 @param label_name: Label that must be set in the jobs, e.g.,
458 'cros-version:butterfly-release/R40-6457.21.0'.
459
460 @returns A summary of the counts of all the passed and failed tests.
461 """
462 job_ids = list(models.Job.objects.filter(
463 name__startswith=job_name_prefix,
464 dependency_labels__name=label_name).values_list(
465 'pk', flat=True))
466 summary = {'passed': 0, 'failed': 0}
467 if not job_ids:
468 return summary
469
470 counts = (tko_models.TestView.objects.filter(
471 afe_job_id__in=job_ids).exclude(
472 test_name='SERVER_JOB').exclude(
473 test_name__startswith='CLIENT_JOB').values(
474 'status').annotate(
475 count=Count('status')))
476 for status in counts:
477 if status['status'] == 'GOOD':
478 summary['passed'] += status['count']
479 else:
480 summary['failed'] += status['count']
481 return summary
482
483
showard2b9a88b2008-06-13 20:55:03 +0000484# profilers
485
486def add_profiler(name, description=None):
487 return models.Profiler.add_object(name=name, description=description).id
488
489
490def modify_profiler(id, **data):
491 models.Profiler.smart_get(id).update_object(data)
492
493
494def delete_profiler(id):
495 models.Profiler.smart_get(id).delete()
496
497
498def get_profilers(**filter_data):
499 return rpc_utils.prepare_for_serialization(
500 models.Profiler.list_objects(filter_data))
501
502
mblighe8819cd2008-02-15 16:48:40 +0000503# users
504
505def add_user(login, access_level=None):
jadmanski0afbb632008-06-06 21:10:57 +0000506 return models.User.add_object(login=login, access_level=access_level).id
mblighe8819cd2008-02-15 16:48:40 +0000507
508
509def modify_user(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000510 models.User.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000511
512
513def delete_user(id):
jadmanski0afbb632008-06-06 21:10:57 +0000514 models.User.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000515
516
517def get_users(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000518 return rpc_utils.prepare_for_serialization(
519 models.User.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000520
521
522# acl groups
523
524def add_acl_group(name, description=None):
showard04f2cd82008-07-25 20:53:31 +0000525 group = models.AclGroup.add_object(name=name, description=description)
showard64a95952010-01-13 21:27:16 +0000526 group.users.add(models.User.current_user())
showard04f2cd82008-07-25 20:53:31 +0000527 return group.id
mblighe8819cd2008-02-15 16:48:40 +0000528
529
530def modify_acl_group(id, **data):
showard04f2cd82008-07-25 20:53:31 +0000531 group = models.AclGroup.smart_get(id)
532 group.check_for_acl_violation_acl_group()
533 group.update_object(data)
534 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000535
536
537def acl_group_add_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000538 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000539 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000540 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000541 group.users.add(*users)
mblighe8819cd2008-02-15 16:48:40 +0000542
543
544def acl_group_remove_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000545 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000546 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000547 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000548 group.users.remove(*users)
showard04f2cd82008-07-25 20:53:31 +0000549 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000550
551
552def acl_group_add_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000553 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000554 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000555 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000556 group.hosts.add(*hosts)
showard08f981b2008-06-24 21:59:03 +0000557 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000558
559
560def acl_group_remove_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000561 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000562 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000563 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000564 group.hosts.remove(*hosts)
showard08f981b2008-06-24 21:59:03 +0000565 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000566
567
568def delete_acl_group(id):
jadmanski0afbb632008-06-06 21:10:57 +0000569 models.AclGroup.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000570
571
572def get_acl_groups(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000573 acl_groups = models.AclGroup.list_objects(filter_data)
574 for acl_group in acl_groups:
575 acl_group_obj = models.AclGroup.objects.get(id=acl_group['id'])
576 acl_group['users'] = [user.login
577 for user in acl_group_obj.users.all()]
578 acl_group['hosts'] = [host.hostname
579 for host in acl_group_obj.hosts.all()]
580 return rpc_utils.prepare_for_serialization(acl_groups)
mblighe8819cd2008-02-15 16:48:40 +0000581
582
583# jobs
584
mbligh120351e2009-01-24 01:40:45 +0000585def generate_control_file(tests=(), kernel=None, label=None, profilers=(),
showard91f85102009-10-12 20:34:52 +0000586 client_control_file='', use_container=False,
showard232b7ae2009-11-10 00:46:48 +0000587 profile_only=None, upload_kernel_config=False):
jadmanski0afbb632008-06-06 21:10:57 +0000588 """
mbligh120351e2009-01-24 01:40:45 +0000589 Generates a client-side control file to load a kernel and run tests.
590
591 @param tests List of tests to run.
mbligha3c58d22009-08-24 22:01:51 +0000592 @param kernel A list of kernel info dictionaries configuring which kernels
593 to boot for this job and other options for them
mbligh120351e2009-01-24 01:40:45 +0000594 @param label Name of label to grab kernel config from.
595 @param profilers List of profilers to activate during the job.
596 @param client_control_file The contents of a client-side control file to
597 run at the end of all tests. If this is supplied, all tests must be
598 client side.
599 TODO: in the future we should support server control files directly
600 to wrap with a kernel. That'll require changing the parameter
601 name and adding a boolean to indicate if it is a client or server
602 control file.
603 @param use_container unused argument today. TODO: Enable containers
604 on the host during a client side test.
showard91f85102009-10-12 20:34:52 +0000605 @param profile_only A boolean that indicates what default profile_only
606 mode to use in the control file. Passing None will generate a
607 control file that does not explcitly set the default mode at all.
showard232b7ae2009-11-10 00:46:48 +0000608 @param upload_kernel_config: if enabled it will generate server control
609 file code that uploads the kernel config file to the client and
610 tells the client of the new (local) path when compiling the kernel;
611 the tests must be server side tests
mbligh120351e2009-01-24 01:40:45 +0000612
613 @returns a dict with the following keys:
614 control_file: str, The control file text.
615 is_server: bool, is the control file a server-side control file?
616 synch_count: How many machines the job uses per autoserv execution.
617 synch_count == 1 means the job is asynchronous.
618 dependencies: A list of the names of labels on which the job depends.
619 """
showardd86debe2009-06-10 17:37:56 +0000620 if not tests and not client_control_file:
showard2bab8f42008-11-12 18:15:22 +0000621 return dict(control_file='', is_server=False, synch_count=1,
showard989f25d2008-10-01 11:38:11 +0000622 dependencies=[])
mblighe8819cd2008-02-15 16:48:40 +0000623
showard989f25d2008-10-01 11:38:11 +0000624 cf_info, test_objects, profiler_objects, label = (
showard2b9a88b2008-06-13 20:55:03 +0000625 rpc_utils.prepare_generate_control_file(tests, kernel, label,
626 profilers))
showard989f25d2008-10-01 11:38:11 +0000627 cf_info['control_file'] = control_file.generate_control(
mbligha3c58d22009-08-24 22:01:51 +0000628 tests=test_objects, kernels=kernel, platform=label,
mbligh120351e2009-01-24 01:40:45 +0000629 profilers=profiler_objects, is_server=cf_info['is_server'],
showard232b7ae2009-11-10 00:46:48 +0000630 client_control_file=client_control_file, profile_only=profile_only,
631 upload_kernel_config=upload_kernel_config)
showard989f25d2008-10-01 11:38:11 +0000632 return cf_info
mblighe8819cd2008-02-15 16:48:40 +0000633
634
jamesren4a41e012010-07-16 22:33:48 +0000635def create_parameterized_job(name, priority, test, parameters, kernel=None,
636 label=None, profilers=(), profiler_parameters=None,
637 use_container=False, profile_only=None,
638 upload_kernel_config=False, hosts=(),
639 meta_hosts=(), one_time_hosts=(),
640 atomic_group_name=None, synch_count=None,
641 is_template=False, timeout=None,
Simran Basi7e605742013-11-12 13:43:36 -0800642 timeout_mins=None, max_runtime_mins=None,
643 run_verify=False, email_list='', dependencies=(),
644 reboot_before=None, reboot_after=None,
645 parse_failed_repair=None, hostless=False,
Dan Shiec1d47d2015-02-13 11:38:13 -0800646 keyvals=None, drone_set=None, run_reset=True,
647 require_ssq=None):
jamesren4a41e012010-07-16 22:33:48 +0000648 """
649 Creates and enqueues a parameterized job.
650
651 Most parameters a combination of the parameters for generate_control_file()
652 and create_job(), with the exception of:
653
654 @param test name or ID of the test to run
655 @param parameters a map of parameter name ->
656 tuple of (param value, param type)
657 @param profiler_parameters a dictionary of parameters for the profilers:
658 key: profiler name
659 value: dict of param name -> tuple of
660 (param value,
661 param type)
662 """
663 # Save the values of the passed arguments here. What we're going to do with
664 # them is pass them all to rpc_utils.get_create_job_common_args(), which
665 # will extract the subset of these arguments that apply for
666 # rpc_utils.create_job_common(), which we then pass in to that function.
667 args = locals()
668
669 # Set up the parameterized job configs
670 test_obj = models.Test.smart_get(test)
Aviv Keshet3dd8beb2013-05-13 17:36:04 -0700671 control_type = test_obj.test_type
jamesren4a41e012010-07-16 22:33:48 +0000672
673 try:
674 label = models.Label.smart_get(label)
675 except models.Label.DoesNotExist:
676 label = None
677
678 kernel_objs = models.Kernel.create_kernels(kernel)
679 profiler_objs = [models.Profiler.smart_get(profiler)
680 for profiler in profilers]
681
682 parameterized_job = models.ParameterizedJob.objects.create(
683 test=test_obj, label=label, use_container=use_container,
684 profile_only=profile_only,
685 upload_kernel_config=upload_kernel_config)
686 parameterized_job.kernels.add(*kernel_objs)
687
688 for profiler in profiler_objs:
689 parameterized_profiler = models.ParameterizedJobProfiler.objects.create(
690 parameterized_job=parameterized_job,
691 profiler=profiler)
692 profiler_params = profiler_parameters.get(profiler.name, {})
693 for name, (value, param_type) in profiler_params.iteritems():
694 models.ParameterizedJobProfilerParameter.objects.create(
695 parameterized_job_profiler=parameterized_profiler,
696 parameter_name=name,
697 parameter_value=value,
698 parameter_type=param_type)
699
700 try:
701 for parameter in test_obj.testparameter_set.all():
702 if parameter.name in parameters:
703 param_value, param_type = parameters.pop(parameter.name)
704 parameterized_job.parameterizedjobparameter_set.create(
705 test_parameter=parameter, parameter_value=param_value,
706 parameter_type=param_type)
707
708 if parameters:
709 raise Exception('Extra parameters remain: %r' % parameters)
710
711 return rpc_utils.create_job_common(
712 parameterized_job=parameterized_job.id,
713 control_type=control_type,
714 **rpc_utils.get_create_job_common_args(args))
715 except:
716 parameterized_job.delete()
717 raise
718
719
Simran Basib6ec8ae2014-04-23 12:05:08 -0700720def create_job_page_handler(name, priority, control_file, control_type,
721 image=None, hostless=False, **kwargs):
722 """\
723 Create and enqueue a job.
724
725 @param name name of this job
726 @param priority Integer priority of this job. Higher is more important.
727 @param control_file String contents of the control file.
728 @param control_type Type of control file, Client or Server.
729 @param kwargs extra args that will be required by create_suite_job or
730 create_job.
731
732 @returns The created Job id number.
733 """
734 control_file = rpc_utils.encode_ascii(control_file)
Jiaxi Luodd67beb2014-07-18 16:28:31 -0700735 if not control_file:
736 raise model_logic.ValidationError({
737 'control_file' : "Control file cannot be empty"})
Simran Basib6ec8ae2014-04-23 12:05:08 -0700738
739 if image and hostless:
740 return site_rpc_interface.create_suite_job(
741 name=name, control_file=control_file, priority=priority,
742 build=image, **kwargs)
743 return create_job(name, priority, control_file, control_type, image=image,
744 hostless=hostless, **kwargs)
745
746
showard12f3e322009-05-13 21:27:42 +0000747def create_job(name, priority, control_file, control_type,
748 hosts=(), meta_hosts=(), one_time_hosts=(),
749 atomic_group_name=None, synch_count=None, is_template=False,
Simran Basi7e605742013-11-12 13:43:36 -0800750 timeout=None, timeout_mins=None, max_runtime_mins=None,
751 run_verify=False, email_list='', dependencies=(),
752 reboot_before=None, reboot_after=None, parse_failed_repair=None,
753 hostless=False, keyvals=None, drone_set=None, image=None,
Dan Shiec1d47d2015-02-13 11:38:13 -0800754 parent_job_id=None, test_retry=0, run_reset=True,
755 require_ssp=None, args=(), **kwargs):
jadmanski0afbb632008-06-06 21:10:57 +0000756 """\
757 Create and enqueue a job.
mblighe8819cd2008-02-15 16:48:40 +0000758
showarda1e74b32009-05-12 17:32:04 +0000759 @param name name of this job
Alex Miller7d658cf2013-09-04 16:00:35 -0700760 @param priority Integer priority of this job. Higher is more important.
showarda1e74b32009-05-12 17:32:04 +0000761 @param control_file String contents of the control file.
762 @param control_type Type of control file, Client or Server.
763 @param synch_count How many machines the job uses per autoserv execution.
Jiaxi Luo90190c92014-06-18 12:35:57 -0700764 synch_count == 1 means the job is asynchronous. If an atomic group is
765 given this value is treated as a minimum.
showarda1e74b32009-05-12 17:32:04 +0000766 @param is_template If true then create a template job.
767 @param timeout Hours after this call returns until the job times out.
Simran Basi7e605742013-11-12 13:43:36 -0800768 @param timeout_mins Minutes after this call returns until the job times
Jiaxi Luo90190c92014-06-18 12:35:57 -0700769 out.
Simran Basi34217022012-11-06 13:43:15 -0800770 @param max_runtime_mins Minutes from job starting time until job times out
showarda1e74b32009-05-12 17:32:04 +0000771 @param run_verify Should the host be verified before running the test?
772 @param email_list String containing emails to mail when the job is done
773 @param dependencies List of label names on which this job depends
774 @param reboot_before Never, If dirty, or Always
775 @param reboot_after Never, If all tests passed, or Always
776 @param parse_failed_repair if true, results of failed repairs launched by
Jiaxi Luo90190c92014-06-18 12:35:57 -0700777 this job will be parsed as part of the job.
showarda9545c02009-12-18 22:44:26 +0000778 @param hostless if true, create a hostless job
showardc1a98d12010-01-15 00:22:22 +0000779 @param keyvals dict of keyvals to associate with the job
showarda1e74b32009-05-12 17:32:04 +0000780 @param hosts List of hosts to run job on.
781 @param meta_hosts List where each entry is a label name, and for each entry
Jiaxi Luo90190c92014-06-18 12:35:57 -0700782 one host will be chosen from that label to run the job on.
showarda1e74b32009-05-12 17:32:04 +0000783 @param one_time_hosts List of hosts not in the database to run the job on.
784 @param atomic_group_name The name of an atomic group to schedule the job on.
jamesren76fcf192010-04-21 20:39:50 +0000785 @param drone_set The name of the drone set to run this test on.
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800786 @param image OS image to install before running job.
Aviv Keshet0b9cfc92013-02-05 11:36:02 -0800787 @param parent_job_id id of a job considered to be parent of created job.
Simran Basib6ec8ae2014-04-23 12:05:08 -0700788 @param test_retry Number of times to retry test if the test did not
Jiaxi Luo90190c92014-06-18 12:35:57 -0700789 complete successfully. (optional, default: 0)
Simran Basib6ec8ae2014-04-23 12:05:08 -0700790 @param run_reset Should the host be reset before running the test?
Dan Shiec1d47d2015-02-13 11:38:13 -0800791 @param require_ssp Set to True to require server-side packaging to run the
792 test. If it's set to None, drone will still try to run
793 the server side with server-side packaging. If the
794 autotest-server package doesn't exist for the build or
795 image is not set, drone will run the test without server-
796 side packaging. Default is None.
Jiaxi Luo90190c92014-06-18 12:35:57 -0700797 @param args A list of args to be injected into control file.
Simran Basib6ec8ae2014-04-23 12:05:08 -0700798 @param kwargs extra keyword args. NOT USED.
showardc92da832009-04-07 18:14:34 +0000799
800 @returns The created Job id number.
jadmanski0afbb632008-06-06 21:10:57 +0000801 """
Jiaxi Luo90190c92014-06-18 12:35:57 -0700802 if args:
803 control_file = tools.inject_vars({'args': args}, control_file)
804
Simran Basiab5a1bf2014-05-28 15:39:44 -0700805 if image is None:
806 return rpc_utils.create_job_common(
807 **rpc_utils.get_create_job_common_args(locals()))
808
809 # When image is supplied use a known parameterized test already in the
810 # database to pass the OS image path from the front end, through the
811 # scheduler, and finally to autoserv as the --image parameter.
812
813 # The test autoupdate_ParameterizedJob is in afe_autotests and used to
814 # instantiate a Test object and from there a ParameterizedJob.
815 known_test_obj = models.Test.smart_get('autoupdate_ParameterizedJob')
816 known_parameterized_job = models.ParameterizedJob.objects.create(
817 test=known_test_obj)
818
819 # autoupdate_ParameterizedJob has a single parameter, the image parameter,
820 # stored in the table afe_test_parameters. We retrieve and set this
821 # instance of the parameter to the OS image path.
822 image_parameter = known_test_obj.testparameter_set.get(test=known_test_obj,
823 name='image')
824 known_parameterized_job.parameterizedjobparameter_set.create(
825 test_parameter=image_parameter, parameter_value=image,
826 parameter_type='string')
827
828 # By passing a parameterized_job to create_job_common the job entry in
829 # the afe_jobs table will have the field parameterized_job_id set.
830 # The scheduler uses this id in the afe_parameterized_jobs table to
831 # match this job to our known test, and then with the
832 # afe_parameterized_job_parameters table to get the actual image path.
jamesren4a41e012010-07-16 22:33:48 +0000833 return rpc_utils.create_job_common(
Simran Basiab5a1bf2014-05-28 15:39:44 -0700834 parameterized_job=known_parameterized_job.id,
jamesren4a41e012010-07-16 22:33:48 +0000835 **rpc_utils.get_create_job_common_args(locals()))
mblighe8819cd2008-02-15 16:48:40 +0000836
837
showard9dbdcda2008-10-14 17:34:36 +0000838def abort_host_queue_entries(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000839 """\
showard9dbdcda2008-10-14 17:34:36 +0000840 Abort a set of host queue entries.
Fang Deng63b0e452014-12-19 14:38:15 -0800841
842 @return: A list of dictionaries, each contains information
843 about an aborted HQE.
jadmanski0afbb632008-06-06 21:10:57 +0000844 """
showard9dbdcda2008-10-14 17:34:36 +0000845 query = models.HostQueueEntry.query_objects(filter_data)
beepsfaecbce2013-10-29 11:35:10 -0700846
847 # Dont allow aborts on:
848 # 1. Jobs that have already completed (whether or not they were aborted)
849 # 2. Jobs that we have already been aborted (but may not have completed)
850 query = query.filter(complete=False).filter(aborted=False)
showarddc817512008-11-12 18:16:41 +0000851 models.AclGroup.check_abort_permissions(query)
showard9dbdcda2008-10-14 17:34:36 +0000852 host_queue_entries = list(query.select_related())
showard2bab8f42008-11-12 18:15:22 +0000853 rpc_utils.check_abort_synchronous_jobs(host_queue_entries)
mblighe8819cd2008-02-15 16:48:40 +0000854
Simran Basic1b26762013-06-26 14:23:21 -0700855 models.HostQueueEntry.abort_host_queue_entries(host_queue_entries)
Fang Deng63b0e452014-12-19 14:38:15 -0800856 hqe_info = [{'HostQueueEntry': hqe.id, 'Job': hqe.job_id,
857 'Job name': hqe.job.name} for hqe in host_queue_entries]
858 return hqe_info
showard9d821ab2008-07-11 16:54:29 +0000859
860
beeps8bb1f7d2013-08-05 01:30:09 -0700861def abort_special_tasks(**filter_data):
862 """\
863 Abort the special task, or tasks, specified in the filter.
864 """
865 query = models.SpecialTask.query_objects(filter_data)
866 special_tasks = query.filter(is_active=True)
867 for task in special_tasks:
868 task.abort()
869
870
Simran Basi73dae552013-02-25 14:57:46 -0800871def _call_special_tasks_on_hosts(task, hosts):
872 """\
873 Schedules a set of hosts for a special task.
874
875 @returns A list of hostnames that a special task was created for.
876 """
877 models.AclGroup.check_for_acl_violation_hosts(hosts)
Prashanth Balasubramanian6edaaf92014-11-24 16:36:25 -0800878 shard_host_map = rpc_utils.bucket_hosts_by_shard(hosts)
Prashanth Balasubramanian8c98ac12014-12-23 11:26:44 -0800879 if shard_host_map and not utils.is_shard():
Prashanth Balasubramanian6edaaf92014-11-24 16:36:25 -0800880 raise ValueError('The following hosts are on shards, please '
881 'follow the link to the shards and create jobs '
882 'there instead. %s.' % shard_host_map)
Simran Basi73dae552013-02-25 14:57:46 -0800883 for host in hosts:
884 models.SpecialTask.schedule_special_task(host, task)
885 return list(sorted(host.hostname for host in hosts))
886
887
showard1ff7b2e2009-05-15 23:17:18 +0000888def reverify_hosts(**filter_data):
889 """\
890 Schedules a set of hosts for verify.
mbligh4e545a52009-12-19 05:30:39 +0000891
892 @returns A list of hostnames that a verify task was created for.
showard1ff7b2e2009-05-15 23:17:18 +0000893 """
Prashanth Balasubramanian40981232014-12-16 19:01:58 -0800894 hosts = models.Host.query_objects(filter_data)
895 shard_host_map = rpc_utils.bucket_hosts_by_shard(hosts, rpc_hostnames=True)
896
897 # Filter out hosts on a shard from those on the master, forward
898 # rpcs to the shard with an additional hostname__in filter, and
899 # create a local SpecialTask for each remaining host.
Prashanth Balasubramanian8c98ac12014-12-23 11:26:44 -0800900 if shard_host_map and not utils.is_shard():
Prashanth Balasubramanian40981232014-12-16 19:01:58 -0800901 hosts = [h for h in hosts if h.shard is None]
902 for shard, hostnames in shard_host_map.iteritems():
903
904 # The main client of this module is the frontend website, and
905 # it invokes it with an 'id' or an 'id__in' filter. Regardless,
906 # the 'hostname' filter should narrow down the list of hosts on
907 # each shard even though we supply all the ids in filter_data.
908 # This method uses hostname instead of id because it fits better
909 # with the overall architecture of redirection functions in rpc_utils.
910 shard_filter = filter_data.copy()
911 shard_filter['hostname__in'] = hostnames
912 rpc_utils.run_rpc_on_multiple_hostnames(
913 'reverify_hosts', [shard], **shard_filter)
914
915 # There is a race condition here if someone assigns a shard to one of these
916 # hosts before we create the task. The host will stay on the master if:
917 # 1. The host is not Ready
918 # 2. The host is Ready but has a task
919 # But if the host is Ready and doesn't have a task yet, it will get sent
920 # to the shard as we're creating a task here.
921
922 # Given that we only rarely verify Ready hosts it isn't worth putting this
923 # entire method in a transaction. The worst case scenario is that we have
924 # a verify running on a Ready host while the shard is using it, if the verify
925 # fails no subsequent tasks will be created against the host on the master,
926 # and verifies are safe enough that this is OK.
927 return _call_special_tasks_on_hosts(models.SpecialTask.Task.VERIFY, hosts)
Simran Basi73dae552013-02-25 14:57:46 -0800928
929
930def repair_hosts(**filter_data):
931 """\
932 Schedules a set of hosts for repair.
933
934 @returns A list of hostnames that a repair task was created for.
935 """
936 return _call_special_tasks_on_hosts(models.SpecialTask.Task.REPAIR,
937 models.Host.query_objects(filter_data))
showard1ff7b2e2009-05-15 23:17:18 +0000938
939
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700940def get_jobs(not_yet_run=False, running=False, finished=False,
941 suite=False, sub=False, standalone=False, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000942 """\
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700943 Extra status filter args for get_jobs:
jadmanski0afbb632008-06-06 21:10:57 +0000944 -not_yet_run: Include only jobs that have not yet started running.
945 -running: Include only jobs that have start running but for which not
946 all hosts have completed.
947 -finished: Include only jobs for which all hosts have completed (or
948 aborted).
949 At most one of these three fields should be specified.
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700950
951 Extra type filter args for get_jobs:
952 -suite: Include only jobs with child jobs.
953 -sub: Include only jobs with a parent job.
954 -standalone: Inlcude only jobs with no child or parent jobs.
955 At most one of these three fields should be specified.
jadmanski0afbb632008-06-06 21:10:57 +0000956 """
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700957 extra_args = rpc_utils.extra_job_status_filters(not_yet_run,
958 running,
959 finished)
960 filter_data['extra_args'] = rpc_utils.extra_job_type_filters(extra_args,
961 suite,
962 sub,
963 standalone)
showard0957a842009-05-11 19:25:08 +0000964 job_dicts = []
965 jobs = list(models.Job.query_objects(filter_data))
966 models.Job.objects.populate_relationships(jobs, models.Label,
967 'dependencies')
showardc1a98d12010-01-15 00:22:22 +0000968 models.Job.objects.populate_relationships(jobs, models.JobKeyval, 'keyvals')
showard0957a842009-05-11 19:25:08 +0000969 for job in jobs:
970 job_dict = job.get_object_dict()
971 job_dict['dependencies'] = ','.join(label.name
972 for label in job.dependencies)
showardc1a98d12010-01-15 00:22:22 +0000973 job_dict['keyvals'] = dict((keyval.key, keyval.value)
974 for keyval in job.keyvals)
Eric Lid23bc192011-02-09 14:38:57 -0800975 if job.parameterized_job:
976 job_dict['image'] = get_parameterized_autoupdate_image_url(job)
showard0957a842009-05-11 19:25:08 +0000977 job_dicts.append(job_dict)
978 return rpc_utils.prepare_for_serialization(job_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000979
980
981def get_num_jobs(not_yet_run=False, running=False, finished=False,
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700982 suite=False, sub=False, standalone=False,
jadmanski0afbb632008-06-06 21:10:57 +0000983 **filter_data):
984 """\
985 See get_jobs() for documentation of extra filter parameters.
986 """
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700987 extra_args = rpc_utils.extra_job_status_filters(not_yet_run,
988 running,
989 finished)
990 filter_data['extra_args'] = rpc_utils.extra_job_type_filters(extra_args,
991 suite,
992 sub,
993 standalone)
jadmanski0afbb632008-06-06 21:10:57 +0000994 return models.Job.query_count(filter_data)
mblighe8819cd2008-02-15 16:48:40 +0000995
996
mblighe8819cd2008-02-15 16:48:40 +0000997def get_jobs_summary(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000998 """\
Jiaxi Luoaac54572014-06-04 13:57:02 -0700999 Like get_jobs(), but adds 'status_counts' and 'result_counts' field.
1000
1001 'status_counts' filed is a dictionary mapping status strings to the number
1002 of hosts currently with that status, i.e. {'Queued' : 4, 'Running' : 2}.
1003
1004 'result_counts' field is piped to tko's rpc_interface and has the return
1005 format specified under get_group_counts.
jadmanski0afbb632008-06-06 21:10:57 +00001006 """
1007 jobs = get_jobs(**filter_data)
1008 ids = [job['id'] for job in jobs]
1009 all_status_counts = models.Job.objects.get_status_counts(ids)
1010 for job in jobs:
1011 job['status_counts'] = all_status_counts[job['id']]
Jiaxi Luoaac54572014-06-04 13:57:02 -07001012 job['result_counts'] = tko_rpc_interface.get_status_counts(
1013 ['afe_job_id', 'afe_job_id'],
1014 header_groups=[['afe_job_id'], ['afe_job_id']],
1015 **{'afe_job_id': job['id']})
jadmanski0afbb632008-06-06 21:10:57 +00001016 return rpc_utils.prepare_for_serialization(jobs)
mblighe8819cd2008-02-15 16:48:40 +00001017
1018
showarda965cef2009-05-15 23:17:41 +00001019def get_info_for_clone(id, preserve_metahosts, queue_entry_filter_data=None):
showarda8709c52008-07-03 19:44:54 +00001020 """\
1021 Retrieves all the information needed to clone a job.
1022 """
showarda8709c52008-07-03 19:44:54 +00001023 job = models.Job.objects.get(id=id)
showard29f7cd22009-04-29 21:16:24 +00001024 job_info = rpc_utils.get_job_info(job,
showarda965cef2009-05-15 23:17:41 +00001025 preserve_metahosts,
1026 queue_entry_filter_data)
showard945072f2008-09-03 20:34:59 +00001027
showardd9992fe2008-07-31 02:15:03 +00001028 host_dicts = []
showard29f7cd22009-04-29 21:16:24 +00001029 for host in job_info['hosts']:
1030 host_dict = get_hosts(id=host.id)[0]
1031 other_labels = host_dict['labels']
1032 if host_dict['platform']:
1033 other_labels.remove(host_dict['platform'])
1034 host_dict['other_labels'] = ', '.join(other_labels)
showardd9992fe2008-07-31 02:15:03 +00001035 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +00001036
showard29f7cd22009-04-29 21:16:24 +00001037 for host in job_info['one_time_hosts']:
1038 host_dict = dict(hostname=host.hostname,
1039 id=host.id,
1040 platform='(one-time host)',
1041 locked_text='')
1042 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +00001043
showard4d077562009-05-08 18:24:36 +00001044 # convert keys from Label objects to strings (names of labels)
showard29f7cd22009-04-29 21:16:24 +00001045 meta_host_counts = dict((meta_host.name, count) for meta_host, count
showard4d077562009-05-08 18:24:36 +00001046 in job_info['meta_host_counts'].iteritems())
showard29f7cd22009-04-29 21:16:24 +00001047
1048 info = dict(job=job.get_object_dict(),
1049 meta_host_counts=meta_host_counts,
1050 hosts=host_dicts)
1051 info['job']['dependencies'] = job_info['dependencies']
1052 if job_info['atomic_group']:
1053 info['atomic_group_name'] = (job_info['atomic_group']).name
1054 else:
1055 info['atomic_group_name'] = None
jamesren2275ef12010-04-12 18:25:06 +00001056 info['hostless'] = job_info['hostless']
jamesren76fcf192010-04-21 20:39:50 +00001057 info['drone_set'] = job.drone_set and job.drone_set.name
showarda8709c52008-07-03 19:44:54 +00001058
Eric Lid23bc192011-02-09 14:38:57 -08001059 if job.parameterized_job:
1060 info['job']['image'] = get_parameterized_autoupdate_image_url(job)
1061
showarda8709c52008-07-03 19:44:54 +00001062 return rpc_utils.prepare_for_serialization(info)
1063
1064
showard34dc5fa2008-04-24 20:58:40 +00001065# host queue entries
1066
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001067def get_host_queue_entries(start_time=None, end_time=None, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +00001068 """\
showardc92da832009-04-07 18:14:34 +00001069 @returns A sequence of nested dictionaries of host and job information.
jadmanski0afbb632008-06-06 21:10:57 +00001070 """
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001071 filter_data = rpc_utils.inject_times_to_filter('started_on__gte',
1072 'started_on__lte',
1073 start_time,
1074 end_time,
1075 **filter_data)
J. Richard Barnetteb5164d62015-04-13 12:59:31 -07001076 return rpc_utils.prepare_rows_as_nested_dicts(
1077 models.HostQueueEntry.query_objects(filter_data),
1078 ('host', 'atomic_group', 'job'))
showard34dc5fa2008-04-24 20:58:40 +00001079
1080
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001081def get_num_host_queue_entries(start_time=None, end_time=None, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +00001082 """\
1083 Get the number of host queue entries associated with this job.
1084 """
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001085 filter_data = rpc_utils.inject_times_to_filter('started_on__gte',
1086 'started_on__lte',
1087 start_time,
1088 end_time,
1089 **filter_data)
jadmanski0afbb632008-06-06 21:10:57 +00001090 return models.HostQueueEntry.query_count(filter_data)
showard34dc5fa2008-04-24 20:58:40 +00001091
1092
showard1e935f12008-07-11 00:11:36 +00001093def get_hqe_percentage_complete(**filter_data):
1094 """
showardc92da832009-04-07 18:14:34 +00001095 Computes the fraction of host queue entries matching the given filter data
showard1e935f12008-07-11 00:11:36 +00001096 that are complete.
1097 """
1098 query = models.HostQueueEntry.query_objects(filter_data)
1099 complete_count = query.filter(complete=True).count()
1100 total_count = query.count()
1101 if total_count == 0:
1102 return 1
1103 return float(complete_count) / total_count
1104
1105
showard1a5a4082009-07-28 20:01:37 +00001106# special tasks
1107
1108def get_special_tasks(**filter_data):
J. Richard Barnetteb5164d62015-04-13 12:59:31 -07001109 """Get special task entries from the local database.
1110
1111 Query the special tasks table for tasks matching the given
1112 `filter_data`, and return a list of the results. No attempt is
1113 made to forward the call to shards; the buck will stop here.
1114 The caller is expected to know the target shard for such reasons
1115 as:
1116 * The caller is a service (such as gs_offloader) configured
1117 to operate on behalf of one specific shard, and no other.
1118 * The caller has a host as a parameter, and knows that this is
1119 the shard assigned to that host.
1120
1121 @param filter_data Filter keywords to pass to the underlying
1122 database query.
1123
1124 """
J. Richard Barnettefdfcd662015-04-13 17:20:29 -07001125 return rpc_utils.prepare_rows_as_nested_dicts(
1126 models.SpecialTask.query_objects(filter_data),
1127 ('host', 'queue_entry'))
J. Richard Barnetteb5164d62015-04-13 12:59:31 -07001128
1129
1130def get_host_special_tasks(host_id, **filter_data):
1131 """Get special task entries for a given host.
1132
1133 Query the special tasks table for tasks that ran on the host
1134 given by `host_id` and matching the given `filter_data`.
1135 Return a list of the results. If the host is assigned to a
1136 shard, forward this call to that shard.
1137
1138 @param host_id Id in the database of the target host.
1139 @param filter_data Filter keywords to pass to the underlying
1140 database query.
1141
1142 """
1143 host = models.Host.smart_get(host_id)
1144 if not host.shard:
J. Richard Barnettefdfcd662015-04-13 17:20:29 -07001145 return get_special_tasks(host_id=host_id, **filter_data)
J. Richard Barnetteb5164d62015-04-13 12:59:31 -07001146 else:
J. Richard Barnette39255fa2015-04-14 17:23:41 -07001147 # The return values from AFE methods are post-processed
1148 # objects that aren't JSON-serializable. So, we have to
1149 # call AFE.run() to get the raw, serializable output from
1150 # the shard.
J. Richard Barnetteb5164d62015-04-13 12:59:31 -07001151 shard_afe = frontend.AFE(server=host.shard.rpc_hostname())
1152 return shard_afe.run('get_special_tasks',
1153 host_id=host_id, **filter_data)
showard1a5a4082009-07-28 20:01:37 +00001154
1155
J. Richard Barnette39255fa2015-04-14 17:23:41 -07001156def get_status_task(host_id, end_time):
J. Richard Barnette4d7e6e62015-05-01 10:47:34 -07001157 """Get the "status task" for a host from the local shard.
J. Richard Barnette39255fa2015-04-14 17:23:41 -07001158
J. Richard Barnette4d7e6e62015-05-01 10:47:34 -07001159 Returns a single special task representing the given host's
1160 "status task". The status task is a completed special task that
1161 identifies whether the corresponding host was working or broken
1162 when it completed. A successful task indicates a working host;
1163 a failed task indicates broken.
J. Richard Barnette39255fa2015-04-14 17:23:41 -07001164
J. Richard Barnette4d7e6e62015-05-01 10:47:34 -07001165 This call will not be forward to a shard; the receiving server
1166 must be the shard that owns the host.
1167
1168 @param host_id Id in the database of the target host.
1169 @param end_time Time reference for the host's status.
1170
1171 @return A single task; its status (successful or not)
1172 corresponds to the status of the host (working or
1173 broken) at the given time. If no task is found, return
1174 `None`.
1175
1176 """
1177 tasklist = rpc_utils.prepare_rows_as_nested_dicts(
1178 status_history.get_status_task(host_id, end_time),
1179 ('host', 'queue_entry'))
1180 return tasklist[0] if tasklist else None
1181
1182
1183def get_host_status_task(host_id, end_time):
1184 """Get the "status task" for a host from its owning shard.
1185
1186 Finds the given host's owning shard, and forwards to it a call
1187 to `get_status_task()` (see above).
J. Richard Barnette39255fa2015-04-14 17:23:41 -07001188
1189 @param host_id Id in the database of the target host.
1190 @param end_time Time reference for the host's status.
1191
1192 @return A single task; its status (successful or not)
1193 corresponds to the status of the host (working or
1194 broken) at the given time. If no task is found, return
1195 `None`.
1196
1197 """
1198 host = models.Host.smart_get(host_id)
1199 if not host.shard:
J. Richard Barnette4d7e6e62015-05-01 10:47:34 -07001200 return get_status_task(host_id, end_time)
J. Richard Barnette39255fa2015-04-14 17:23:41 -07001201 else:
1202 # The return values from AFE methods are post-processed
1203 # objects that aren't JSON-serializable. So, we have to
1204 # call AFE.run() to get the raw, serializable output from
1205 # the shard.
1206 shard_afe = frontend.AFE(server=host.shard.rpc_hostname())
1207 return shard_afe.run('get_status_task',
1208 host_id=host_id, end_time=end_time)
1209
1210
showardc0ac3a72009-07-08 21:14:45 +00001211# support for host detail view
1212
Jiaxi Luo79ce6422014-06-13 17:08:09 -07001213def get_host_queue_entries_and_special_tasks(host_id, query_start=None,
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001214 query_limit=None, start_time=None,
1215 end_time=None):
showardc0ac3a72009-07-08 21:14:45 +00001216 """
1217 @returns an interleaved list of HostQueueEntries and SpecialTasks,
1218 in approximate run order. each dict contains keys for type, host,
1219 job, status, started_on, execution_path, and ID.
1220 """
1221 total_limit = None
1222 if query_limit is not None:
1223 total_limit = query_start + query_limit
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001224 filter_data_common = {'host': host_id,
1225 'query_limit': total_limit,
1226 'sort_by': ['-id']}
showardc0ac3a72009-07-08 21:14:45 +00001227
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001228 filter_data_queue_entries, filter_data_special_tasks = (
1229 rpc_utils.inject_times_to_hqe_special_tasks_filters(
1230 filter_data_common, start_time, end_time))
1231
1232 queue_entries = list(models.HostQueueEntry.query_objects(
1233 filter_data_queue_entries))
1234 special_tasks = list(models.SpecialTask.query_objects(
1235 filter_data_special_tasks))
showardc0ac3a72009-07-08 21:14:45 +00001236
1237 interleaved_entries = rpc_utils.interleave_entries(queue_entries,
1238 special_tasks)
1239 if query_start is not None:
1240 interleaved_entries = interleaved_entries[query_start:]
1241 if query_limit is not None:
1242 interleaved_entries = interleaved_entries[:query_limit]
1243 return rpc_utils.prepare_for_serialization(interleaved_entries)
1244
1245
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001246def get_num_host_queue_entries_and_special_tasks(host_id, start_time=None,
1247 end_time=None):
1248 filter_data_common = {'host': host_id}
1249
1250 filter_data_queue_entries, filter_data_special_tasks = (
1251 rpc_utils.inject_times_to_hqe_special_tasks_filters(
1252 filter_data_common, start_time, end_time))
1253
1254 return (models.HostQueueEntry.query_count(filter_data_queue_entries)
1255 + models.SpecialTask.query_count(filter_data_special_tasks))
showardc0ac3a72009-07-08 21:14:45 +00001256
1257
showard29f7cd22009-04-29 21:16:24 +00001258# recurring run
1259
1260def get_recurring(**filter_data):
1261 return rpc_utils.prepare_rows_as_nested_dicts(
1262 models.RecurringRun.query_objects(filter_data),
1263 ('job', 'owner'))
1264
1265
1266def get_num_recurring(**filter_data):
1267 return models.RecurringRun.query_count(filter_data)
1268
1269
1270def delete_recurring_runs(**filter_data):
1271 to_delete = models.RecurringRun.query_objects(filter_data)
1272 to_delete.delete()
1273
1274
1275def create_recurring_run(job_id, start_date, loop_period, loop_count):
showard64a95952010-01-13 21:27:16 +00001276 owner = models.User.current_user().login
showard29f7cd22009-04-29 21:16:24 +00001277 job = models.Job.objects.get(id=job_id)
1278 return job.create_recurring_job(start_date=start_date,
1279 loop_period=loop_period,
1280 loop_count=loop_count,
1281 owner=owner)
1282
1283
mblighe8819cd2008-02-15 16:48:40 +00001284# other
1285
showarde0b63622008-08-04 20:58:47 +00001286def echo(data=""):
1287 """\
1288 Returns a passed in string. For doing a basic test to see if RPC calls
1289 can successfully be made.
1290 """
1291 return data
1292
1293
showardb7a52fd2009-04-27 20:10:56 +00001294def get_motd():
1295 """\
1296 Returns the message of the day as a string.
1297 """
1298 return rpc_utils.get_motd()
1299
1300
mblighe8819cd2008-02-15 16:48:40 +00001301def get_static_data():
jadmanski0afbb632008-06-06 21:10:57 +00001302 """\
1303 Returns a dictionary containing a bunch of data that shouldn't change
1304 often and is otherwise inaccessible. This includes:
showardc92da832009-04-07 18:14:34 +00001305
1306 priorities: List of job priority choices.
1307 default_priority: Default priority value for new jobs.
1308 users: Sorted list of all users.
Jiaxi Luo31874592014-06-11 10:36:35 -07001309 labels: Sorted list of labels not start with 'cros-version' and
1310 'fw-version'.
showardc92da832009-04-07 18:14:34 +00001311 atomic_groups: Sorted list of all atomic groups.
1312 tests: Sorted list of all tests.
1313 profilers: Sorted list of all profilers.
1314 current_user: Logged-in username.
1315 host_statuses: Sorted list of possible Host statuses.
1316 job_statuses: Sorted list of possible HostQueueEntry statuses.
Simran Basi7e605742013-11-12 13:43:36 -08001317 job_timeout_default: The default job timeout length in minutes.
showarda1e74b32009-05-12 17:32:04 +00001318 parse_failed_repair_default: Default value for the parse_failed_repair job
Jiaxi Luo31874592014-06-11 10:36:35 -07001319 option.
showardc92da832009-04-07 18:14:34 +00001320 reboot_before_options: A list of valid RebootBefore string enums.
1321 reboot_after_options: A list of valid RebootAfter string enums.
1322 motd: Server's message of the day.
1323 status_dictionary: A mapping from one word job status names to a more
1324 informative description.
jadmanski0afbb632008-06-06 21:10:57 +00001325 """
showard21baa452008-10-21 00:08:39 +00001326
1327 job_fields = models.Job.get_field_dict()
jamesren76fcf192010-04-21 20:39:50 +00001328 default_drone_set_name = models.DroneSet.default_drone_set_name()
1329 drone_sets = ([default_drone_set_name] +
1330 sorted(drone_set.name for drone_set in
1331 models.DroneSet.objects.exclude(
1332 name=default_drone_set_name)))
showard21baa452008-10-21 00:08:39 +00001333
jadmanski0afbb632008-06-06 21:10:57 +00001334 result = {}
Alex Miller7d658cf2013-09-04 16:00:35 -07001335 result['priorities'] = priorities.Priority.choices()
1336 default_priority = priorities.Priority.DEFAULT
1337 result['default_priority'] = 'Default'
1338 result['max_schedulable_priority'] = priorities.Priority.DEFAULT
jadmanski0afbb632008-06-06 21:10:57 +00001339 result['users'] = get_users(sort_by=['login'])
Jiaxi Luo31874592014-06-11 10:36:35 -07001340
1341 label_exclude_filters = [{'name__startswith': 'cros-version'},
1342 {'name__startswith': 'fw-version'}]
1343 result['labels'] = get_labels(
1344 label_exclude_filters,
1345 sort_by=['-platform', 'name'])
1346
showardc92da832009-04-07 18:14:34 +00001347 result['atomic_groups'] = get_atomic_groups(sort_by=['name'])
jadmanski0afbb632008-06-06 21:10:57 +00001348 result['tests'] = get_tests(sort_by=['name'])
showard2b9a88b2008-06-13 20:55:03 +00001349 result['profilers'] = get_profilers(sort_by=['name'])
showard0fc38302008-10-23 00:44:07 +00001350 result['current_user'] = rpc_utils.prepare_for_serialization(
showard64a95952010-01-13 21:27:16 +00001351 models.User.current_user().get_object_dict())
showard2b9a88b2008-06-13 20:55:03 +00001352 result['host_statuses'] = sorted(models.Host.Status.names)
mbligh5a198b92008-12-11 19:33:29 +00001353 result['job_statuses'] = sorted(models.HostQueueEntry.Status.names)
Simran Basi7e605742013-11-12 13:43:36 -08001354 result['job_timeout_mins_default'] = models.Job.DEFAULT_TIMEOUT_MINS
Simran Basi34217022012-11-06 13:43:15 -08001355 result['job_max_runtime_mins_default'] = (
1356 models.Job.DEFAULT_MAX_RUNTIME_MINS)
showarda1e74b32009-05-12 17:32:04 +00001357 result['parse_failed_repair_default'] = bool(
1358 models.Job.DEFAULT_PARSE_FAILED_REPAIR)
jamesrendd855242010-03-02 22:23:44 +00001359 result['reboot_before_options'] = model_attributes.RebootBefore.names
1360 result['reboot_after_options'] = model_attributes.RebootAfter.names
showard8fbae652009-01-20 23:23:10 +00001361 result['motd'] = rpc_utils.get_motd()
jamesren76fcf192010-04-21 20:39:50 +00001362 result['drone_sets_enabled'] = models.DroneSet.drone_sets_enabled()
1363 result['drone_sets'] = drone_sets
jamesren4a41e012010-07-16 22:33:48 +00001364 result['parameterized_jobs'] = models.Job.parameterized_jobs_enabled()
showard8ac29b42008-07-17 17:01:55 +00001365
showardd3dc1992009-04-22 21:01:40 +00001366 result['status_dictionary'] = {"Aborted": "Aborted",
showard8ac29b42008-07-17 17:01:55 +00001367 "Verifying": "Verifying Host",
Alex Millerdfff2fd2013-05-28 13:05:06 -07001368 "Provisioning": "Provisioning Host",
showard8ac29b42008-07-17 17:01:55 +00001369 "Pending": "Waiting on other hosts",
1370 "Running": "Running autoserv",
1371 "Completed": "Autoserv completed",
1372 "Failed": "Failed to complete",
showardd823b362008-07-24 16:35:46 +00001373 "Queued": "Queued",
showard5deb6772008-11-04 21:54:33 +00001374 "Starting": "Next in host's queue",
1375 "Stopped": "Other host(s) failed verify",
showardd3dc1992009-04-22 21:01:40 +00001376 "Parsing": "Awaiting parse of final results",
showard29f7cd22009-04-29 21:16:24 +00001377 "Gathering": "Gathering log files",
showard8cc058f2009-09-08 16:26:33 +00001378 "Template": "Template job for recurring run",
mbligh4608b002010-01-05 18:22:35 +00001379 "Waiting": "Waiting for scheduler action",
Dan Shi07e09af2013-04-12 09:31:29 -07001380 "Archiving": "Archiving results",
1381 "Resetting": "Resetting hosts"}
Jiaxi Luo421608e2014-07-07 14:38:00 -07001382
1383 result['wmatrix_url'] = rpc_utils.get_wmatrix_url()
Simran Basi71206ef2014-08-13 13:51:18 -07001384 result['is_moblab'] = bool(utils.is_moblab())
Jiaxi Luo421608e2014-07-07 14:38:00 -07001385
jadmanski0afbb632008-06-06 21:10:57 +00001386 return result
showard29f7cd22009-04-29 21:16:24 +00001387
1388
1389def get_server_time():
1390 return datetime.datetime.now().strftime("%Y-%m-%d %H:%M")