blob: 352e77f4715279e7a58506a46a1be31fe79a5f70 [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
showarddf062562008-07-03 19:56:37 +0000242def add_host(hostname, status=None, locked=None, protection=None):
jadmanski0afbb632008-06-06 21:10:57 +0000243 return models.Host.add_object(hostname=hostname, status=status,
showarddf062562008-07-03 19:56:37 +0000244 locked=locked, protection=protection).id
mblighe8819cd2008-02-15 16:48:40 +0000245
246
Jakob Juelich50e91f72014-10-01 12:43:23 -0700247@rpc_utils.forward_single_host_rpc_to_shard
mblighe8819cd2008-02-15 16:48:40 +0000248def modify_host(id, **data):
Jakob Juelich50e91f72014-10-01 12:43:23 -0700249 """Modify local attributes of a host.
250
251 If this is called on the master, but the host is assigned to a shard, this
252 will also forward the call to the responsible shard. This means i.e. if a
253 host is being locked using this function, this change will also propagate to
254 shards.
255
256 @param id: id of the host to modify.
257 @param **data: key=value pairs of values to set on the host.
258 """
showardbe0d8692009-08-20 23:42:44 +0000259 rpc_utils.check_modify_host(data)
showardce7c0922009-09-11 18:39:24 +0000260 host = models.Host.smart_get(id)
Jakob Juelich50e91f72014-10-01 12:43:23 -0700261
showardce7c0922009-09-11 18:39:24 +0000262 rpc_utils.check_modify_host_locking(host, data)
263 host.update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000264
265
showard276f9442009-05-20 00:33:16 +0000266def modify_hosts(host_filter_data, update_data):
Jakob Juelich50e91f72014-10-01 12:43:23 -0700267 """Modify local attributes of multiple hosts.
268
269 If this is called on the master, but one of the hosts in that match the
270 filters is assigned to a shard, this will also forward the call to the
271 responsible shard.
272
273 The filters are always applied on the master, not on the shards. This means
274 if the states of a host differ on the master and a shard, the state on the
275 master will be used. I.e. this means:
276 A host was synced to Shard 1. On Shard 1 the status of the host was set to
277 'Repair Failed'.
278 - A call to modify_hosts with host_filter_data={'status': 'Ready'} will
279 update the host (both on the shard and on the master), because the state
280 of the host as the master knows it is still 'Ready'.
281 - A call to modify_hosts with host_filter_data={'status': 'Repair failed'
282 will not update the host, because the filter doesn't apply on the master.
283
showardbe0d8692009-08-20 23:42:44 +0000284 @param host_filter_data: Filters out which hosts to modify.
285 @param update_data: A dictionary with the changes to make to the hosts.
showard276f9442009-05-20 00:33:16 +0000286 """
showardbe0d8692009-08-20 23:42:44 +0000287 rpc_utils.check_modify_host(update_data)
showard276f9442009-05-20 00:33:16 +0000288 hosts = models.Host.query_objects(host_filter_data)
Jakob Juelich50e91f72014-10-01 12:43:23 -0700289
290 affected_shard_hostnames = set()
291 affected_host_ids = []
292
Alex Miller9658a952013-05-14 16:40:02 -0700293 # Check all hosts before changing data for exception safety.
294 for host in hosts:
295 rpc_utils.check_modify_host_locking(host, update_data)
Jakob Juelich50e91f72014-10-01 12:43:23 -0700296 if host.shard:
Prashanth Balasubramanian8c98ac12014-12-23 11:26:44 -0800297 affected_shard_hostnames.add(host.shard.rpc_hostname())
Jakob Juelich50e91f72014-10-01 12:43:23 -0700298 affected_host_ids.append(host.id)
299
Prashanth Balasubramanian8c98ac12014-12-23 11:26:44 -0800300 if not utils.is_shard():
Jakob Juelich50e91f72014-10-01 12:43:23 -0700301 # Caution: Changing the filter from the original here. See docstring.
302 rpc_utils.run_rpc_on_multiple_hostnames(
303 'modify_hosts', affected_shard_hostnames,
304 host_filter_data={'id__in': affected_host_ids},
305 update_data=update_data)
306
showard276f9442009-05-20 00:33:16 +0000307 for host in hosts:
308 host.update_object(update_data)
309
310
mblighe8819cd2008-02-15 16:48:40 +0000311def host_add_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000312 labels = models.Label.smart_get_bulk(labels)
showardcafd16e2009-05-29 18:37:49 +0000313 host = models.Host.smart_get(id)
314
315 platforms = [label.name for label in labels if label.platform]
316 if len(platforms) > 1:
317 raise model_logic.ValidationError(
318 {'labels': 'Adding more than one platform label: %s' %
319 ', '.join(platforms)})
320 if len(platforms) == 1:
321 models.Host.check_no_platform([host])
322 host.labels.add(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000323
324
325def host_remove_labels(id, labels):
showardbe3ec042008-11-12 18:16:07 +0000326 labels = models.Label.smart_get_bulk(labels)
jadmanski0afbb632008-06-06 21:10:57 +0000327 models.Host.smart_get(id).labels.remove(*labels)
mblighe8819cd2008-02-15 16:48:40 +0000328
329
MK Ryuacf35922014-10-03 14:56:49 -0700330def get_host_attribute(attribute, **host_filter_data):
331 """
332 @param attribute: string name of attribute
333 @param host_filter_data: filter data to apply to Hosts to choose hosts to
334 act upon
335 """
336 hosts = rpc_utils.get_host_query((), False, False, True, host_filter_data)
337 hosts = list(hosts)
338 models.Host.objects.populate_relationships(hosts, models.HostAttribute,
339 'attribute_list')
340 host_attr_dicts = []
341 for host_obj in hosts:
342 for attr_obj in host_obj.attribute_list:
343 if attr_obj.attribute == attribute:
344 host_attr_dicts.append(attr_obj.get_object_dict())
345 return rpc_utils.prepare_for_serialization(host_attr_dicts)
346
347
showard0957a842009-05-11 19:25:08 +0000348def set_host_attribute(attribute, value, **host_filter_data):
349 """
350 @param attribute string name of attribute
351 @param value string, or None to delete an attribute
352 @param host_filter_data filter data to apply to Hosts to choose hosts to act
353 upon
354 """
355 assert host_filter_data # disallow accidental actions on all hosts
356 hosts = models.Host.query_objects(host_filter_data)
357 models.AclGroup.check_for_acl_violation_hosts(hosts)
358
359 for host in hosts:
showardf8b19042009-05-12 17:22:49 +0000360 host.set_or_delete_attribute(attribute, value)
showard0957a842009-05-11 19:25:08 +0000361
362
Jakob Juelich50e91f72014-10-01 12:43:23 -0700363@rpc_utils.forward_single_host_rpc_to_shard
mblighe8819cd2008-02-15 16:48:40 +0000364def delete_host(id):
jadmanski0afbb632008-06-06 21:10:57 +0000365 models.Host.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000366
367
showard87cc38f2009-08-20 23:37:04 +0000368def get_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000369 exclude_atomic_group_hosts=False, valid_only=True, **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000370 """
371 @param multiple_labels: match hosts in all of the labels given. Should
372 be a list of label names.
373 @param exclude_only_if_needed_labels: Exclude hosts with at least one
374 "only_if_needed" label applied.
375 @param exclude_atomic_group_hosts: Exclude hosts that have one or more
376 atomic group labels associated with them.
jadmanski0afbb632008-06-06 21:10:57 +0000377 """
showard43a3d262008-11-12 18:17:05 +0000378 hosts = rpc_utils.get_host_query(multiple_labels,
379 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000380 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000381 valid_only, filter_data)
showard0957a842009-05-11 19:25:08 +0000382 hosts = list(hosts)
383 models.Host.objects.populate_relationships(hosts, models.Label,
384 'label_list')
385 models.Host.objects.populate_relationships(hosts, models.AclGroup,
386 'acl_list')
387 models.Host.objects.populate_relationships(hosts, models.HostAttribute,
388 'attribute_list')
showard43a3d262008-11-12 18:17:05 +0000389 host_dicts = []
390 for host_obj in hosts:
391 host_dict = host_obj.get_object_dict()
showard0957a842009-05-11 19:25:08 +0000392 host_dict['labels'] = [label.name for label in host_obj.label_list]
showard909c9142009-07-07 20:54:42 +0000393 host_dict['platform'], host_dict['atomic_group'] = (rpc_utils.
394 find_platform_and_atomic_group(host_obj))
showard0957a842009-05-11 19:25:08 +0000395 host_dict['acls'] = [acl.name for acl in host_obj.acl_list]
396 host_dict['attributes'] = dict((attribute.attribute, attribute.value)
397 for attribute in host_obj.attribute_list)
showard43a3d262008-11-12 18:17:05 +0000398 host_dicts.append(host_dict)
399 return rpc_utils.prepare_for_serialization(host_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000400
401
showard87cc38f2009-08-20 23:37:04 +0000402def get_num_hosts(multiple_labels=(), exclude_only_if_needed_labels=False,
showard8aa84fc2009-09-16 17:17:55 +0000403 exclude_atomic_group_hosts=False, valid_only=True,
404 **filter_data):
showard87cc38f2009-08-20 23:37:04 +0000405 """
406 Same parameters as get_hosts().
407
408 @returns The number of matching hosts.
409 """
showard43a3d262008-11-12 18:17:05 +0000410 hosts = rpc_utils.get_host_query(multiple_labels,
411 exclude_only_if_needed_labels,
showard87cc38f2009-08-20 23:37:04 +0000412 exclude_atomic_group_hosts,
showard8aa84fc2009-09-16 17:17:55 +0000413 valid_only, filter_data)
showard43a3d262008-11-12 18:17:05 +0000414 return hosts.count()
showard1385b162008-03-13 15:59:40 +0000415
mblighe8819cd2008-02-15 16:48:40 +0000416
417# tests
418
showard909c7a62008-07-15 21:52:38 +0000419def add_test(name, test_type, path, author=None, dependencies=None,
showard3d9899a2008-07-31 02:11:58 +0000420 experimental=True, run_verify=None, test_class=None,
showard909c7a62008-07-15 21:52:38 +0000421 test_time=None, test_category=None, description=None,
422 sync_count=1):
jadmanski0afbb632008-06-06 21:10:57 +0000423 return models.Test.add_object(name=name, test_type=test_type, path=path,
showard909c7a62008-07-15 21:52:38 +0000424 author=author, dependencies=dependencies,
425 experimental=experimental,
426 run_verify=run_verify, test_time=test_time,
427 test_category=test_category,
428 sync_count=sync_count,
jadmanski0afbb632008-06-06 21:10:57 +0000429 test_class=test_class,
430 description=description).id
mblighe8819cd2008-02-15 16:48:40 +0000431
432
433def modify_test(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000434 models.Test.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000435
436
437def delete_test(id):
jadmanski0afbb632008-06-06 21:10:57 +0000438 models.Test.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000439
440
441def get_tests(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000442 return rpc_utils.prepare_for_serialization(
443 models.Test.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000444
445
Moises Osorio2dc7a102014-12-02 18:24:02 -0800446@_timer.decorate
447def get_tests_status_counts_by_job_name_label(job_name_prefix, label_name):
448 """Gets the counts of all passed and failed tests from the matching jobs.
449
450 @param job_name_prefix: Name prefix of the jobs to get the summary from, e.g.,
451 'butterfly-release/R40-6457.21.0/bvt-cq/'.
452 @param label_name: Label that must be set in the jobs, e.g.,
453 'cros-version:butterfly-release/R40-6457.21.0'.
454
455 @returns A summary of the counts of all the passed and failed tests.
456 """
457 job_ids = list(models.Job.objects.filter(
458 name__startswith=job_name_prefix,
459 dependency_labels__name=label_name).values_list(
460 'pk', flat=True))
461 summary = {'passed': 0, 'failed': 0}
462 if not job_ids:
463 return summary
464
465 counts = (tko_models.TestView.objects.filter(
466 afe_job_id__in=job_ids).exclude(
467 test_name='SERVER_JOB').exclude(
468 test_name__startswith='CLIENT_JOB').values(
469 'status').annotate(
470 count=Count('status')))
471 for status in counts:
472 if status['status'] == 'GOOD':
473 summary['passed'] += status['count']
474 else:
475 summary['failed'] += status['count']
476 return summary
477
478
showard2b9a88b2008-06-13 20:55:03 +0000479# profilers
480
481def add_profiler(name, description=None):
482 return models.Profiler.add_object(name=name, description=description).id
483
484
485def modify_profiler(id, **data):
486 models.Profiler.smart_get(id).update_object(data)
487
488
489def delete_profiler(id):
490 models.Profiler.smart_get(id).delete()
491
492
493def get_profilers(**filter_data):
494 return rpc_utils.prepare_for_serialization(
495 models.Profiler.list_objects(filter_data))
496
497
mblighe8819cd2008-02-15 16:48:40 +0000498# users
499
500def add_user(login, access_level=None):
jadmanski0afbb632008-06-06 21:10:57 +0000501 return models.User.add_object(login=login, access_level=access_level).id
mblighe8819cd2008-02-15 16:48:40 +0000502
503
504def modify_user(id, **data):
jadmanski0afbb632008-06-06 21:10:57 +0000505 models.User.smart_get(id).update_object(data)
mblighe8819cd2008-02-15 16:48:40 +0000506
507
508def delete_user(id):
jadmanski0afbb632008-06-06 21:10:57 +0000509 models.User.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000510
511
512def get_users(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000513 return rpc_utils.prepare_for_serialization(
514 models.User.list_objects(filter_data))
mblighe8819cd2008-02-15 16:48:40 +0000515
516
517# acl groups
518
519def add_acl_group(name, description=None):
showard04f2cd82008-07-25 20:53:31 +0000520 group = models.AclGroup.add_object(name=name, description=description)
showard64a95952010-01-13 21:27:16 +0000521 group.users.add(models.User.current_user())
showard04f2cd82008-07-25 20:53:31 +0000522 return group.id
mblighe8819cd2008-02-15 16:48:40 +0000523
524
525def modify_acl_group(id, **data):
showard04f2cd82008-07-25 20:53:31 +0000526 group = models.AclGroup.smart_get(id)
527 group.check_for_acl_violation_acl_group()
528 group.update_object(data)
529 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000530
531
532def acl_group_add_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000533 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000534 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000535 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000536 group.users.add(*users)
mblighe8819cd2008-02-15 16:48:40 +0000537
538
539def acl_group_remove_users(id, users):
jadmanski0afbb632008-06-06 21:10:57 +0000540 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000541 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000542 users = models.User.smart_get_bulk(users)
jadmanski0afbb632008-06-06 21:10:57 +0000543 group.users.remove(*users)
showard04f2cd82008-07-25 20:53:31 +0000544 group.add_current_user_if_empty()
mblighe8819cd2008-02-15 16:48:40 +0000545
546
547def acl_group_add_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000548 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000549 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000550 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000551 group.hosts.add(*hosts)
showard08f981b2008-06-24 21:59:03 +0000552 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000553
554
555def acl_group_remove_hosts(id, hosts):
jadmanski0afbb632008-06-06 21:10:57 +0000556 group = models.AclGroup.smart_get(id)
showard04f2cd82008-07-25 20:53:31 +0000557 group.check_for_acl_violation_acl_group()
showardbe3ec042008-11-12 18:16:07 +0000558 hosts = models.Host.smart_get_bulk(hosts)
jadmanski0afbb632008-06-06 21:10:57 +0000559 group.hosts.remove(*hosts)
showard08f981b2008-06-24 21:59:03 +0000560 group.on_host_membership_change()
mblighe8819cd2008-02-15 16:48:40 +0000561
562
563def delete_acl_group(id):
jadmanski0afbb632008-06-06 21:10:57 +0000564 models.AclGroup.smart_get(id).delete()
mblighe8819cd2008-02-15 16:48:40 +0000565
566
567def get_acl_groups(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000568 acl_groups = models.AclGroup.list_objects(filter_data)
569 for acl_group in acl_groups:
570 acl_group_obj = models.AclGroup.objects.get(id=acl_group['id'])
571 acl_group['users'] = [user.login
572 for user in acl_group_obj.users.all()]
573 acl_group['hosts'] = [host.hostname
574 for host in acl_group_obj.hosts.all()]
575 return rpc_utils.prepare_for_serialization(acl_groups)
mblighe8819cd2008-02-15 16:48:40 +0000576
577
578# jobs
579
mbligh120351e2009-01-24 01:40:45 +0000580def generate_control_file(tests=(), kernel=None, label=None, profilers=(),
showard91f85102009-10-12 20:34:52 +0000581 client_control_file='', use_container=False,
showard232b7ae2009-11-10 00:46:48 +0000582 profile_only=None, upload_kernel_config=False):
jadmanski0afbb632008-06-06 21:10:57 +0000583 """
mbligh120351e2009-01-24 01:40:45 +0000584 Generates a client-side control file to load a kernel and run tests.
585
586 @param tests List of tests to run.
mbligha3c58d22009-08-24 22:01:51 +0000587 @param kernel A list of kernel info dictionaries configuring which kernels
588 to boot for this job and other options for them
mbligh120351e2009-01-24 01:40:45 +0000589 @param label Name of label to grab kernel config from.
590 @param profilers List of profilers to activate during the job.
591 @param client_control_file The contents of a client-side control file to
592 run at the end of all tests. If this is supplied, all tests must be
593 client side.
594 TODO: in the future we should support server control files directly
595 to wrap with a kernel. That'll require changing the parameter
596 name and adding a boolean to indicate if it is a client or server
597 control file.
598 @param use_container unused argument today. TODO: Enable containers
599 on the host during a client side test.
showard91f85102009-10-12 20:34:52 +0000600 @param profile_only A boolean that indicates what default profile_only
601 mode to use in the control file. Passing None will generate a
602 control file that does not explcitly set the default mode at all.
showard232b7ae2009-11-10 00:46:48 +0000603 @param upload_kernel_config: if enabled it will generate server control
604 file code that uploads the kernel config file to the client and
605 tells the client of the new (local) path when compiling the kernel;
606 the tests must be server side tests
mbligh120351e2009-01-24 01:40:45 +0000607
608 @returns a dict with the following keys:
609 control_file: str, The control file text.
610 is_server: bool, is the control file a server-side control file?
611 synch_count: How many machines the job uses per autoserv execution.
612 synch_count == 1 means the job is asynchronous.
613 dependencies: A list of the names of labels on which the job depends.
614 """
showardd86debe2009-06-10 17:37:56 +0000615 if not tests and not client_control_file:
showard2bab8f42008-11-12 18:15:22 +0000616 return dict(control_file='', is_server=False, synch_count=1,
showard989f25d2008-10-01 11:38:11 +0000617 dependencies=[])
mblighe8819cd2008-02-15 16:48:40 +0000618
showard989f25d2008-10-01 11:38:11 +0000619 cf_info, test_objects, profiler_objects, label = (
showard2b9a88b2008-06-13 20:55:03 +0000620 rpc_utils.prepare_generate_control_file(tests, kernel, label,
621 profilers))
showard989f25d2008-10-01 11:38:11 +0000622 cf_info['control_file'] = control_file.generate_control(
mbligha3c58d22009-08-24 22:01:51 +0000623 tests=test_objects, kernels=kernel, platform=label,
mbligh120351e2009-01-24 01:40:45 +0000624 profilers=profiler_objects, is_server=cf_info['is_server'],
showard232b7ae2009-11-10 00:46:48 +0000625 client_control_file=client_control_file, profile_only=profile_only,
626 upload_kernel_config=upload_kernel_config)
showard989f25d2008-10-01 11:38:11 +0000627 return cf_info
mblighe8819cd2008-02-15 16:48:40 +0000628
629
jamesren4a41e012010-07-16 22:33:48 +0000630def create_parameterized_job(name, priority, test, parameters, kernel=None,
631 label=None, profilers=(), profiler_parameters=None,
632 use_container=False, profile_only=None,
633 upload_kernel_config=False, hosts=(),
634 meta_hosts=(), one_time_hosts=(),
635 atomic_group_name=None, synch_count=None,
636 is_template=False, timeout=None,
Simran Basi7e605742013-11-12 13:43:36 -0800637 timeout_mins=None, max_runtime_mins=None,
638 run_verify=False, email_list='', dependencies=(),
639 reboot_before=None, reboot_after=None,
640 parse_failed_repair=None, hostless=False,
Dan Shiec1d47d2015-02-13 11:38:13 -0800641 keyvals=None, drone_set=None, run_reset=True,
642 require_ssq=None):
jamesren4a41e012010-07-16 22:33:48 +0000643 """
644 Creates and enqueues a parameterized job.
645
646 Most parameters a combination of the parameters for generate_control_file()
647 and create_job(), with the exception of:
648
649 @param test name or ID of the test to run
650 @param parameters a map of parameter name ->
651 tuple of (param value, param type)
652 @param profiler_parameters a dictionary of parameters for the profilers:
653 key: profiler name
654 value: dict of param name -> tuple of
655 (param value,
656 param type)
657 """
658 # Save the values of the passed arguments here. What we're going to do with
659 # them is pass them all to rpc_utils.get_create_job_common_args(), which
660 # will extract the subset of these arguments that apply for
661 # rpc_utils.create_job_common(), which we then pass in to that function.
662 args = locals()
663
664 # Set up the parameterized job configs
665 test_obj = models.Test.smart_get(test)
Aviv Keshet3dd8beb2013-05-13 17:36:04 -0700666 control_type = test_obj.test_type
jamesren4a41e012010-07-16 22:33:48 +0000667
668 try:
669 label = models.Label.smart_get(label)
670 except models.Label.DoesNotExist:
671 label = None
672
673 kernel_objs = models.Kernel.create_kernels(kernel)
674 profiler_objs = [models.Profiler.smart_get(profiler)
675 for profiler in profilers]
676
677 parameterized_job = models.ParameterizedJob.objects.create(
678 test=test_obj, label=label, use_container=use_container,
679 profile_only=profile_only,
680 upload_kernel_config=upload_kernel_config)
681 parameterized_job.kernels.add(*kernel_objs)
682
683 for profiler in profiler_objs:
684 parameterized_profiler = models.ParameterizedJobProfiler.objects.create(
685 parameterized_job=parameterized_job,
686 profiler=profiler)
687 profiler_params = profiler_parameters.get(profiler.name, {})
688 for name, (value, param_type) in profiler_params.iteritems():
689 models.ParameterizedJobProfilerParameter.objects.create(
690 parameterized_job_profiler=parameterized_profiler,
691 parameter_name=name,
692 parameter_value=value,
693 parameter_type=param_type)
694
695 try:
696 for parameter in test_obj.testparameter_set.all():
697 if parameter.name in parameters:
698 param_value, param_type = parameters.pop(parameter.name)
699 parameterized_job.parameterizedjobparameter_set.create(
700 test_parameter=parameter, parameter_value=param_value,
701 parameter_type=param_type)
702
703 if parameters:
704 raise Exception('Extra parameters remain: %r' % parameters)
705
706 return rpc_utils.create_job_common(
707 parameterized_job=parameterized_job.id,
708 control_type=control_type,
709 **rpc_utils.get_create_job_common_args(args))
710 except:
711 parameterized_job.delete()
712 raise
713
714
Simran Basib6ec8ae2014-04-23 12:05:08 -0700715def create_job_page_handler(name, priority, control_file, control_type,
716 image=None, hostless=False, **kwargs):
717 """\
718 Create and enqueue a job.
719
720 @param name name of this job
721 @param priority Integer priority of this job. Higher is more important.
722 @param control_file String contents of the control file.
723 @param control_type Type of control file, Client or Server.
724 @param kwargs extra args that will be required by create_suite_job or
725 create_job.
726
727 @returns The created Job id number.
728 """
729 control_file = rpc_utils.encode_ascii(control_file)
Jiaxi Luodd67beb2014-07-18 16:28:31 -0700730 if not control_file:
731 raise model_logic.ValidationError({
732 'control_file' : "Control file cannot be empty"})
Simran Basib6ec8ae2014-04-23 12:05:08 -0700733
734 if image and hostless:
735 return site_rpc_interface.create_suite_job(
736 name=name, control_file=control_file, priority=priority,
737 build=image, **kwargs)
738 return create_job(name, priority, control_file, control_type, image=image,
739 hostless=hostless, **kwargs)
740
741
showard12f3e322009-05-13 21:27:42 +0000742def create_job(name, priority, control_file, control_type,
743 hosts=(), meta_hosts=(), one_time_hosts=(),
744 atomic_group_name=None, synch_count=None, is_template=False,
Simran Basi7e605742013-11-12 13:43:36 -0800745 timeout=None, timeout_mins=None, max_runtime_mins=None,
746 run_verify=False, email_list='', dependencies=(),
747 reboot_before=None, reboot_after=None, parse_failed_repair=None,
748 hostless=False, keyvals=None, drone_set=None, image=None,
Dan Shiec1d47d2015-02-13 11:38:13 -0800749 parent_job_id=None, test_retry=0, run_reset=True,
750 require_ssp=None, args=(), **kwargs):
jadmanski0afbb632008-06-06 21:10:57 +0000751 """\
752 Create and enqueue a job.
mblighe8819cd2008-02-15 16:48:40 +0000753
showarda1e74b32009-05-12 17:32:04 +0000754 @param name name of this job
Alex Miller7d658cf2013-09-04 16:00:35 -0700755 @param priority Integer priority of this job. Higher is more important.
showarda1e74b32009-05-12 17:32:04 +0000756 @param control_file String contents of the control file.
757 @param control_type Type of control file, Client or Server.
758 @param synch_count How many machines the job uses per autoserv execution.
Jiaxi Luo90190c92014-06-18 12:35:57 -0700759 synch_count == 1 means the job is asynchronous. If an atomic group is
760 given this value is treated as a minimum.
showarda1e74b32009-05-12 17:32:04 +0000761 @param is_template If true then create a template job.
762 @param timeout Hours after this call returns until the job times out.
Simran Basi7e605742013-11-12 13:43:36 -0800763 @param timeout_mins Minutes after this call returns until the job times
Jiaxi Luo90190c92014-06-18 12:35:57 -0700764 out.
Simran Basi34217022012-11-06 13:43:15 -0800765 @param max_runtime_mins Minutes from job starting time until job times out
showarda1e74b32009-05-12 17:32:04 +0000766 @param run_verify Should the host be verified before running the test?
767 @param email_list String containing emails to mail when the job is done
768 @param dependencies List of label names on which this job depends
769 @param reboot_before Never, If dirty, or Always
770 @param reboot_after Never, If all tests passed, or Always
771 @param parse_failed_repair if true, results of failed repairs launched by
Jiaxi Luo90190c92014-06-18 12:35:57 -0700772 this job will be parsed as part of the job.
showarda9545c02009-12-18 22:44:26 +0000773 @param hostless if true, create a hostless job
showardc1a98d12010-01-15 00:22:22 +0000774 @param keyvals dict of keyvals to associate with the job
showarda1e74b32009-05-12 17:32:04 +0000775 @param hosts List of hosts to run job on.
776 @param meta_hosts List where each entry is a label name, and for each entry
Jiaxi Luo90190c92014-06-18 12:35:57 -0700777 one host will be chosen from that label to run the job on.
showarda1e74b32009-05-12 17:32:04 +0000778 @param one_time_hosts List of hosts not in the database to run the job on.
779 @param atomic_group_name The name of an atomic group to schedule the job on.
jamesren76fcf192010-04-21 20:39:50 +0000780 @param drone_set The name of the drone set to run this test on.
Paul Pendlebury5a8c6ad2011-02-01 07:20:17 -0800781 @param image OS image to install before running job.
Aviv Keshet0b9cfc92013-02-05 11:36:02 -0800782 @param parent_job_id id of a job considered to be parent of created job.
Simran Basib6ec8ae2014-04-23 12:05:08 -0700783 @param test_retry Number of times to retry test if the test did not
Jiaxi Luo90190c92014-06-18 12:35:57 -0700784 complete successfully. (optional, default: 0)
Simran Basib6ec8ae2014-04-23 12:05:08 -0700785 @param run_reset Should the host be reset before running the test?
Dan Shiec1d47d2015-02-13 11:38:13 -0800786 @param require_ssp Set to True to require server-side packaging to run the
787 test. If it's set to None, drone will still try to run
788 the server side with server-side packaging. If the
789 autotest-server package doesn't exist for the build or
790 image is not set, drone will run the test without server-
791 side packaging. Default is None.
Jiaxi Luo90190c92014-06-18 12:35:57 -0700792 @param args A list of args to be injected into control file.
Simran Basib6ec8ae2014-04-23 12:05:08 -0700793 @param kwargs extra keyword args. NOT USED.
showardc92da832009-04-07 18:14:34 +0000794
795 @returns The created Job id number.
jadmanski0afbb632008-06-06 21:10:57 +0000796 """
Jiaxi Luo90190c92014-06-18 12:35:57 -0700797 if args:
798 control_file = tools.inject_vars({'args': args}, control_file)
799
Simran Basiab5a1bf2014-05-28 15:39:44 -0700800 if image is None:
801 return rpc_utils.create_job_common(
802 **rpc_utils.get_create_job_common_args(locals()))
803
804 # When image is supplied use a known parameterized test already in the
805 # database to pass the OS image path from the front end, through the
806 # scheduler, and finally to autoserv as the --image parameter.
807
808 # The test autoupdate_ParameterizedJob is in afe_autotests and used to
809 # instantiate a Test object and from there a ParameterizedJob.
810 known_test_obj = models.Test.smart_get('autoupdate_ParameterizedJob')
811 known_parameterized_job = models.ParameterizedJob.objects.create(
812 test=known_test_obj)
813
814 # autoupdate_ParameterizedJob has a single parameter, the image parameter,
815 # stored in the table afe_test_parameters. We retrieve and set this
816 # instance of the parameter to the OS image path.
817 image_parameter = known_test_obj.testparameter_set.get(test=known_test_obj,
818 name='image')
819 known_parameterized_job.parameterizedjobparameter_set.create(
820 test_parameter=image_parameter, parameter_value=image,
821 parameter_type='string')
822
823 # By passing a parameterized_job to create_job_common the job entry in
824 # the afe_jobs table will have the field parameterized_job_id set.
825 # The scheduler uses this id in the afe_parameterized_jobs table to
826 # match this job to our known test, and then with the
827 # afe_parameterized_job_parameters table to get the actual image path.
jamesren4a41e012010-07-16 22:33:48 +0000828 return rpc_utils.create_job_common(
Simran Basiab5a1bf2014-05-28 15:39:44 -0700829 parameterized_job=known_parameterized_job.id,
jamesren4a41e012010-07-16 22:33:48 +0000830 **rpc_utils.get_create_job_common_args(locals()))
mblighe8819cd2008-02-15 16:48:40 +0000831
832
showard9dbdcda2008-10-14 17:34:36 +0000833def abort_host_queue_entries(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000834 """\
showard9dbdcda2008-10-14 17:34:36 +0000835 Abort a set of host queue entries.
Fang Deng63b0e452014-12-19 14:38:15 -0800836
837 @return: A list of dictionaries, each contains information
838 about an aborted HQE.
jadmanski0afbb632008-06-06 21:10:57 +0000839 """
showard9dbdcda2008-10-14 17:34:36 +0000840 query = models.HostQueueEntry.query_objects(filter_data)
beepsfaecbce2013-10-29 11:35:10 -0700841
842 # Dont allow aborts on:
843 # 1. Jobs that have already completed (whether or not they were aborted)
844 # 2. Jobs that we have already been aborted (but may not have completed)
845 query = query.filter(complete=False).filter(aborted=False)
showarddc817512008-11-12 18:16:41 +0000846 models.AclGroup.check_abort_permissions(query)
showard9dbdcda2008-10-14 17:34:36 +0000847 host_queue_entries = list(query.select_related())
showard2bab8f42008-11-12 18:15:22 +0000848 rpc_utils.check_abort_synchronous_jobs(host_queue_entries)
mblighe8819cd2008-02-15 16:48:40 +0000849
Simran Basic1b26762013-06-26 14:23:21 -0700850 models.HostQueueEntry.abort_host_queue_entries(host_queue_entries)
Fang Deng63b0e452014-12-19 14:38:15 -0800851 hqe_info = [{'HostQueueEntry': hqe.id, 'Job': hqe.job_id,
852 'Job name': hqe.job.name} for hqe in host_queue_entries]
853 return hqe_info
showard9d821ab2008-07-11 16:54:29 +0000854
855
beeps8bb1f7d2013-08-05 01:30:09 -0700856def abort_special_tasks(**filter_data):
857 """\
858 Abort the special task, or tasks, specified in the filter.
859 """
860 query = models.SpecialTask.query_objects(filter_data)
861 special_tasks = query.filter(is_active=True)
862 for task in special_tasks:
863 task.abort()
864
865
Simran Basi73dae552013-02-25 14:57:46 -0800866def _call_special_tasks_on_hosts(task, hosts):
867 """\
868 Schedules a set of hosts for a special task.
869
870 @returns A list of hostnames that a special task was created for.
871 """
872 models.AclGroup.check_for_acl_violation_hosts(hosts)
Prashanth Balasubramanian6edaaf92014-11-24 16:36:25 -0800873 shard_host_map = rpc_utils.bucket_hosts_by_shard(hosts)
Prashanth Balasubramanian8c98ac12014-12-23 11:26:44 -0800874 if shard_host_map and not utils.is_shard():
Prashanth Balasubramanian6edaaf92014-11-24 16:36:25 -0800875 raise ValueError('The following hosts are on shards, please '
876 'follow the link to the shards and create jobs '
877 'there instead. %s.' % shard_host_map)
Simran Basi73dae552013-02-25 14:57:46 -0800878 for host in hosts:
879 models.SpecialTask.schedule_special_task(host, task)
880 return list(sorted(host.hostname for host in hosts))
881
882
showard1ff7b2e2009-05-15 23:17:18 +0000883def reverify_hosts(**filter_data):
884 """\
885 Schedules a set of hosts for verify.
mbligh4e545a52009-12-19 05:30:39 +0000886
887 @returns A list of hostnames that a verify task was created for.
showard1ff7b2e2009-05-15 23:17:18 +0000888 """
Prashanth Balasubramanian40981232014-12-16 19:01:58 -0800889 hosts = models.Host.query_objects(filter_data)
890 shard_host_map = rpc_utils.bucket_hosts_by_shard(hosts, rpc_hostnames=True)
891
892 # Filter out hosts on a shard from those on the master, forward
893 # rpcs to the shard with an additional hostname__in filter, and
894 # create a local SpecialTask for each remaining host.
Prashanth Balasubramanian8c98ac12014-12-23 11:26:44 -0800895 if shard_host_map and not utils.is_shard():
Prashanth Balasubramanian40981232014-12-16 19:01:58 -0800896 hosts = [h for h in hosts if h.shard is None]
897 for shard, hostnames in shard_host_map.iteritems():
898
899 # The main client of this module is the frontend website, and
900 # it invokes it with an 'id' or an 'id__in' filter. Regardless,
901 # the 'hostname' filter should narrow down the list of hosts on
902 # each shard even though we supply all the ids in filter_data.
903 # This method uses hostname instead of id because it fits better
904 # with the overall architecture of redirection functions in rpc_utils.
905 shard_filter = filter_data.copy()
906 shard_filter['hostname__in'] = hostnames
907 rpc_utils.run_rpc_on_multiple_hostnames(
908 'reverify_hosts', [shard], **shard_filter)
909
910 # There is a race condition here if someone assigns a shard to one of these
911 # hosts before we create the task. The host will stay on the master if:
912 # 1. The host is not Ready
913 # 2. The host is Ready but has a task
914 # But if the host is Ready and doesn't have a task yet, it will get sent
915 # to the shard as we're creating a task here.
916
917 # Given that we only rarely verify Ready hosts it isn't worth putting this
918 # entire method in a transaction. The worst case scenario is that we have
919 # a verify running on a Ready host while the shard is using it, if the verify
920 # fails no subsequent tasks will be created against the host on the master,
921 # and verifies are safe enough that this is OK.
922 return _call_special_tasks_on_hosts(models.SpecialTask.Task.VERIFY, hosts)
Simran Basi73dae552013-02-25 14:57:46 -0800923
924
925def repair_hosts(**filter_data):
926 """\
927 Schedules a set of hosts for repair.
928
929 @returns A list of hostnames that a repair task was created for.
930 """
931 return _call_special_tasks_on_hosts(models.SpecialTask.Task.REPAIR,
932 models.Host.query_objects(filter_data))
showard1ff7b2e2009-05-15 23:17:18 +0000933
934
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700935def get_jobs(not_yet_run=False, running=False, finished=False,
936 suite=False, sub=False, standalone=False, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000937 """\
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700938 Extra status filter args for get_jobs:
jadmanski0afbb632008-06-06 21:10:57 +0000939 -not_yet_run: Include only jobs that have not yet started running.
940 -running: Include only jobs that have start running but for which not
941 all hosts have completed.
942 -finished: Include only jobs for which all hosts have completed (or
943 aborted).
944 At most one of these three fields should be specified.
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700945
946 Extra type filter args for get_jobs:
947 -suite: Include only jobs with child jobs.
948 -sub: Include only jobs with a parent job.
949 -standalone: Inlcude only jobs with no child or parent jobs.
950 At most one of these three fields should be specified.
jadmanski0afbb632008-06-06 21:10:57 +0000951 """
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700952 extra_args = rpc_utils.extra_job_status_filters(not_yet_run,
953 running,
954 finished)
955 filter_data['extra_args'] = rpc_utils.extra_job_type_filters(extra_args,
956 suite,
957 sub,
958 standalone)
showard0957a842009-05-11 19:25:08 +0000959 job_dicts = []
960 jobs = list(models.Job.query_objects(filter_data))
961 models.Job.objects.populate_relationships(jobs, models.Label,
962 'dependencies')
showardc1a98d12010-01-15 00:22:22 +0000963 models.Job.objects.populate_relationships(jobs, models.JobKeyval, 'keyvals')
showard0957a842009-05-11 19:25:08 +0000964 for job in jobs:
965 job_dict = job.get_object_dict()
966 job_dict['dependencies'] = ','.join(label.name
967 for label in job.dependencies)
showardc1a98d12010-01-15 00:22:22 +0000968 job_dict['keyvals'] = dict((keyval.key, keyval.value)
969 for keyval in job.keyvals)
Eric Lid23bc192011-02-09 14:38:57 -0800970 if job.parameterized_job:
971 job_dict['image'] = get_parameterized_autoupdate_image_url(job)
showard0957a842009-05-11 19:25:08 +0000972 job_dicts.append(job_dict)
973 return rpc_utils.prepare_for_serialization(job_dicts)
mblighe8819cd2008-02-15 16:48:40 +0000974
975
976def get_num_jobs(not_yet_run=False, running=False, finished=False,
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700977 suite=False, sub=False, standalone=False,
jadmanski0afbb632008-06-06 21:10:57 +0000978 **filter_data):
979 """\
980 See get_jobs() for documentation of extra filter parameters.
981 """
Jiaxi Luo15cbf372014-07-01 19:20:20 -0700982 extra_args = rpc_utils.extra_job_status_filters(not_yet_run,
983 running,
984 finished)
985 filter_data['extra_args'] = rpc_utils.extra_job_type_filters(extra_args,
986 suite,
987 sub,
988 standalone)
jadmanski0afbb632008-06-06 21:10:57 +0000989 return models.Job.query_count(filter_data)
mblighe8819cd2008-02-15 16:48:40 +0000990
991
mblighe8819cd2008-02-15 16:48:40 +0000992def get_jobs_summary(**filter_data):
jadmanski0afbb632008-06-06 21:10:57 +0000993 """\
Jiaxi Luoaac54572014-06-04 13:57:02 -0700994 Like get_jobs(), but adds 'status_counts' and 'result_counts' field.
995
996 'status_counts' filed is a dictionary mapping status strings to the number
997 of hosts currently with that status, i.e. {'Queued' : 4, 'Running' : 2}.
998
999 'result_counts' field is piped to tko's rpc_interface and has the return
1000 format specified under get_group_counts.
jadmanski0afbb632008-06-06 21:10:57 +00001001 """
1002 jobs = get_jobs(**filter_data)
1003 ids = [job['id'] for job in jobs]
1004 all_status_counts = models.Job.objects.get_status_counts(ids)
1005 for job in jobs:
1006 job['status_counts'] = all_status_counts[job['id']]
Jiaxi Luoaac54572014-06-04 13:57:02 -07001007 job['result_counts'] = tko_rpc_interface.get_status_counts(
1008 ['afe_job_id', 'afe_job_id'],
1009 header_groups=[['afe_job_id'], ['afe_job_id']],
1010 **{'afe_job_id': job['id']})
jadmanski0afbb632008-06-06 21:10:57 +00001011 return rpc_utils.prepare_for_serialization(jobs)
mblighe8819cd2008-02-15 16:48:40 +00001012
1013
showarda965cef2009-05-15 23:17:41 +00001014def get_info_for_clone(id, preserve_metahosts, queue_entry_filter_data=None):
showarda8709c52008-07-03 19:44:54 +00001015 """\
1016 Retrieves all the information needed to clone a job.
1017 """
showarda8709c52008-07-03 19:44:54 +00001018 job = models.Job.objects.get(id=id)
showard29f7cd22009-04-29 21:16:24 +00001019 job_info = rpc_utils.get_job_info(job,
showarda965cef2009-05-15 23:17:41 +00001020 preserve_metahosts,
1021 queue_entry_filter_data)
showard945072f2008-09-03 20:34:59 +00001022
showardd9992fe2008-07-31 02:15:03 +00001023 host_dicts = []
showard29f7cd22009-04-29 21:16:24 +00001024 for host in job_info['hosts']:
1025 host_dict = get_hosts(id=host.id)[0]
1026 other_labels = host_dict['labels']
1027 if host_dict['platform']:
1028 other_labels.remove(host_dict['platform'])
1029 host_dict['other_labels'] = ', '.join(other_labels)
showardd9992fe2008-07-31 02:15:03 +00001030 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +00001031
showard29f7cd22009-04-29 21:16:24 +00001032 for host in job_info['one_time_hosts']:
1033 host_dict = dict(hostname=host.hostname,
1034 id=host.id,
1035 platform='(one-time host)',
1036 locked_text='')
1037 host_dicts.append(host_dict)
showarda8709c52008-07-03 19:44:54 +00001038
showard4d077562009-05-08 18:24:36 +00001039 # convert keys from Label objects to strings (names of labels)
showard29f7cd22009-04-29 21:16:24 +00001040 meta_host_counts = dict((meta_host.name, count) for meta_host, count
showard4d077562009-05-08 18:24:36 +00001041 in job_info['meta_host_counts'].iteritems())
showard29f7cd22009-04-29 21:16:24 +00001042
1043 info = dict(job=job.get_object_dict(),
1044 meta_host_counts=meta_host_counts,
1045 hosts=host_dicts)
1046 info['job']['dependencies'] = job_info['dependencies']
1047 if job_info['atomic_group']:
1048 info['atomic_group_name'] = (job_info['atomic_group']).name
1049 else:
1050 info['atomic_group_name'] = None
jamesren2275ef12010-04-12 18:25:06 +00001051 info['hostless'] = job_info['hostless']
jamesren76fcf192010-04-21 20:39:50 +00001052 info['drone_set'] = job.drone_set and job.drone_set.name
showarda8709c52008-07-03 19:44:54 +00001053
Eric Lid23bc192011-02-09 14:38:57 -08001054 if job.parameterized_job:
1055 info['job']['image'] = get_parameterized_autoupdate_image_url(job)
1056
showarda8709c52008-07-03 19:44:54 +00001057 return rpc_utils.prepare_for_serialization(info)
1058
1059
showard34dc5fa2008-04-24 20:58:40 +00001060# host queue entries
1061
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001062def get_host_queue_entries(start_time=None, end_time=None, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +00001063 """\
showardc92da832009-04-07 18:14:34 +00001064 @returns A sequence of nested dictionaries of host and job information.
jadmanski0afbb632008-06-06 21:10:57 +00001065 """
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001066 filter_data = rpc_utils.inject_times_to_filter('started_on__gte',
1067 'started_on__lte',
1068 start_time,
1069 end_time,
1070 **filter_data)
J. Richard Barnetteb5164d62015-04-13 12:59:31 -07001071 return rpc_utils.prepare_rows_as_nested_dicts(
1072 models.HostQueueEntry.query_objects(filter_data),
1073 ('host', 'atomic_group', 'job'))
showard34dc5fa2008-04-24 20:58:40 +00001074
1075
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001076def get_num_host_queue_entries(start_time=None, end_time=None, **filter_data):
jadmanski0afbb632008-06-06 21:10:57 +00001077 """\
1078 Get the number of host queue entries associated with this job.
1079 """
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001080 filter_data = rpc_utils.inject_times_to_filter('started_on__gte',
1081 'started_on__lte',
1082 start_time,
1083 end_time,
1084 **filter_data)
jadmanski0afbb632008-06-06 21:10:57 +00001085 return models.HostQueueEntry.query_count(filter_data)
showard34dc5fa2008-04-24 20:58:40 +00001086
1087
showard1e935f12008-07-11 00:11:36 +00001088def get_hqe_percentage_complete(**filter_data):
1089 """
showardc92da832009-04-07 18:14:34 +00001090 Computes the fraction of host queue entries matching the given filter data
showard1e935f12008-07-11 00:11:36 +00001091 that are complete.
1092 """
1093 query = models.HostQueueEntry.query_objects(filter_data)
1094 complete_count = query.filter(complete=True).count()
1095 total_count = query.count()
1096 if total_count == 0:
1097 return 1
1098 return float(complete_count) / total_count
1099
1100
showard1a5a4082009-07-28 20:01:37 +00001101# special tasks
1102
1103def get_special_tasks(**filter_data):
J. Richard Barnetteb5164d62015-04-13 12:59:31 -07001104 """Get special task entries from the local database.
1105
1106 Query the special tasks table for tasks matching the given
1107 `filter_data`, and return a list of the results. No attempt is
1108 made to forward the call to shards; the buck will stop here.
1109 The caller is expected to know the target shard for such reasons
1110 as:
1111 * The caller is a service (such as gs_offloader) configured
1112 to operate on behalf of one specific shard, and no other.
1113 * The caller has a host as a parameter, and knows that this is
1114 the shard assigned to that host.
1115
1116 @param filter_data Filter keywords to pass to the underlying
1117 database query.
1118
1119 """
J. Richard Barnettefdfcd662015-04-13 17:20:29 -07001120 return rpc_utils.prepare_rows_as_nested_dicts(
1121 models.SpecialTask.query_objects(filter_data),
1122 ('host', 'queue_entry'))
J. Richard Barnetteb5164d62015-04-13 12:59:31 -07001123
1124
1125def get_host_special_tasks(host_id, **filter_data):
1126 """Get special task entries for a given host.
1127
1128 Query the special tasks table for tasks that ran on the host
1129 given by `host_id` and matching the given `filter_data`.
1130 Return a list of the results. If the host is assigned to a
1131 shard, forward this call to that shard.
1132
1133 @param host_id Id in the database of the target host.
1134 @param filter_data Filter keywords to pass to the underlying
1135 database query.
1136
1137 """
1138 host = models.Host.smart_get(host_id)
1139 if not host.shard:
J. Richard Barnettefdfcd662015-04-13 17:20:29 -07001140 return get_special_tasks(host_id=host_id, **filter_data)
J. Richard Barnetteb5164d62015-04-13 12:59:31 -07001141 else:
J. Richard Barnette39255fa2015-04-14 17:23:41 -07001142 # The return values from AFE methods are post-processed
1143 # objects that aren't JSON-serializable. So, we have to
1144 # call AFE.run() to get the raw, serializable output from
1145 # the shard.
J. Richard Barnetteb5164d62015-04-13 12:59:31 -07001146 shard_afe = frontend.AFE(server=host.shard.rpc_hostname())
1147 return shard_afe.run('get_special_tasks',
1148 host_id=host_id, **filter_data)
showard1a5a4082009-07-28 20:01:37 +00001149
1150
J. Richard Barnette39255fa2015-04-14 17:23:41 -07001151def get_status_task(host_id, end_time):
1152 """Get the special task identifying a host's status.
1153
1154 Returns information for a single special task for a host
1155 identifying whether the host was working or broken at the given
1156 `end_time`. A successful task indicates a working host; a
1157 failed task indicates a broken host.
1158
1159 If the host is managed by a shard, this call forwards the
1160 request.
1161
1162 @param host_id Id in the database of the target host.
1163 @param end_time Time reference for the host's status.
1164
1165 @return A single task; its status (successful or not)
1166 corresponds to the status of the host (working or
1167 broken) at the given time. If no task is found, return
1168 `None`.
1169
1170 """
1171 host = models.Host.smart_get(host_id)
1172 if not host.shard:
1173 tasklist = rpc_utils.prepare_rows_as_nested_dicts(
1174 status_history.get_status_task(host_id, end_time),
1175 ('host', 'queue_entry'))
1176 if tasklist:
1177 return tasklist[0]
1178 else:
1179 return None
1180 else:
1181 # The return values from AFE methods are post-processed
1182 # objects that aren't JSON-serializable. So, we have to
1183 # call AFE.run() to get the raw, serializable output from
1184 # the shard.
1185 shard_afe = frontend.AFE(server=host.shard.rpc_hostname())
1186 return shard_afe.run('get_status_task',
1187 host_id=host_id, end_time=end_time)
1188
1189
showardc0ac3a72009-07-08 21:14:45 +00001190# support for host detail view
1191
Jiaxi Luo79ce6422014-06-13 17:08:09 -07001192def get_host_queue_entries_and_special_tasks(host_id, query_start=None,
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001193 query_limit=None, start_time=None,
1194 end_time=None):
showardc0ac3a72009-07-08 21:14:45 +00001195 """
1196 @returns an interleaved list of HostQueueEntries and SpecialTasks,
1197 in approximate run order. each dict contains keys for type, host,
1198 job, status, started_on, execution_path, and ID.
1199 """
1200 total_limit = None
1201 if query_limit is not None:
1202 total_limit = query_start + query_limit
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001203 filter_data_common = {'host': host_id,
1204 'query_limit': total_limit,
1205 'sort_by': ['-id']}
showardc0ac3a72009-07-08 21:14:45 +00001206
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001207 filter_data_queue_entries, filter_data_special_tasks = (
1208 rpc_utils.inject_times_to_hqe_special_tasks_filters(
1209 filter_data_common, start_time, end_time))
1210
1211 queue_entries = list(models.HostQueueEntry.query_objects(
1212 filter_data_queue_entries))
1213 special_tasks = list(models.SpecialTask.query_objects(
1214 filter_data_special_tasks))
showardc0ac3a72009-07-08 21:14:45 +00001215
1216 interleaved_entries = rpc_utils.interleave_entries(queue_entries,
1217 special_tasks)
1218 if query_start is not None:
1219 interleaved_entries = interleaved_entries[query_start:]
1220 if query_limit is not None:
1221 interleaved_entries = interleaved_entries[:query_limit]
1222 return rpc_utils.prepare_for_serialization(interleaved_entries)
1223
1224
Jiaxi Luo57bc1952014-07-22 15:27:30 -07001225def get_num_host_queue_entries_and_special_tasks(host_id, start_time=None,
1226 end_time=None):
1227 filter_data_common = {'host': host_id}
1228
1229 filter_data_queue_entries, filter_data_special_tasks = (
1230 rpc_utils.inject_times_to_hqe_special_tasks_filters(
1231 filter_data_common, start_time, end_time))
1232
1233 return (models.HostQueueEntry.query_count(filter_data_queue_entries)
1234 + models.SpecialTask.query_count(filter_data_special_tasks))
showardc0ac3a72009-07-08 21:14:45 +00001235
1236
showard29f7cd22009-04-29 21:16:24 +00001237# recurring run
1238
1239def get_recurring(**filter_data):
1240 return rpc_utils.prepare_rows_as_nested_dicts(
1241 models.RecurringRun.query_objects(filter_data),
1242 ('job', 'owner'))
1243
1244
1245def get_num_recurring(**filter_data):
1246 return models.RecurringRun.query_count(filter_data)
1247
1248
1249def delete_recurring_runs(**filter_data):
1250 to_delete = models.RecurringRun.query_objects(filter_data)
1251 to_delete.delete()
1252
1253
1254def create_recurring_run(job_id, start_date, loop_period, loop_count):
showard64a95952010-01-13 21:27:16 +00001255 owner = models.User.current_user().login
showard29f7cd22009-04-29 21:16:24 +00001256 job = models.Job.objects.get(id=job_id)
1257 return job.create_recurring_job(start_date=start_date,
1258 loop_period=loop_period,
1259 loop_count=loop_count,
1260 owner=owner)
1261
1262
mblighe8819cd2008-02-15 16:48:40 +00001263# other
1264
showarde0b63622008-08-04 20:58:47 +00001265def echo(data=""):
1266 """\
1267 Returns a passed in string. For doing a basic test to see if RPC calls
1268 can successfully be made.
1269 """
1270 return data
1271
1272
showardb7a52fd2009-04-27 20:10:56 +00001273def get_motd():
1274 """\
1275 Returns the message of the day as a string.
1276 """
1277 return rpc_utils.get_motd()
1278
1279
mblighe8819cd2008-02-15 16:48:40 +00001280def get_static_data():
jadmanski0afbb632008-06-06 21:10:57 +00001281 """\
1282 Returns a dictionary containing a bunch of data that shouldn't change
1283 often and is otherwise inaccessible. This includes:
showardc92da832009-04-07 18:14:34 +00001284
1285 priorities: List of job priority choices.
1286 default_priority: Default priority value for new jobs.
1287 users: Sorted list of all users.
Jiaxi Luo31874592014-06-11 10:36:35 -07001288 labels: Sorted list of labels not start with 'cros-version' and
1289 'fw-version'.
showardc92da832009-04-07 18:14:34 +00001290 atomic_groups: Sorted list of all atomic groups.
1291 tests: Sorted list of all tests.
1292 profilers: Sorted list of all profilers.
1293 current_user: Logged-in username.
1294 host_statuses: Sorted list of possible Host statuses.
1295 job_statuses: Sorted list of possible HostQueueEntry statuses.
Simran Basi7e605742013-11-12 13:43:36 -08001296 job_timeout_default: The default job timeout length in minutes.
showarda1e74b32009-05-12 17:32:04 +00001297 parse_failed_repair_default: Default value for the parse_failed_repair job
Jiaxi Luo31874592014-06-11 10:36:35 -07001298 option.
showardc92da832009-04-07 18:14:34 +00001299 reboot_before_options: A list of valid RebootBefore string enums.
1300 reboot_after_options: A list of valid RebootAfter string enums.
1301 motd: Server's message of the day.
1302 status_dictionary: A mapping from one word job status names to a more
1303 informative description.
jadmanski0afbb632008-06-06 21:10:57 +00001304 """
showard21baa452008-10-21 00:08:39 +00001305
1306 job_fields = models.Job.get_field_dict()
jamesren76fcf192010-04-21 20:39:50 +00001307 default_drone_set_name = models.DroneSet.default_drone_set_name()
1308 drone_sets = ([default_drone_set_name] +
1309 sorted(drone_set.name for drone_set in
1310 models.DroneSet.objects.exclude(
1311 name=default_drone_set_name)))
showard21baa452008-10-21 00:08:39 +00001312
jadmanski0afbb632008-06-06 21:10:57 +00001313 result = {}
Alex Miller7d658cf2013-09-04 16:00:35 -07001314 result['priorities'] = priorities.Priority.choices()
1315 default_priority = priorities.Priority.DEFAULT
1316 result['default_priority'] = 'Default'
1317 result['max_schedulable_priority'] = priorities.Priority.DEFAULT
jadmanski0afbb632008-06-06 21:10:57 +00001318 result['users'] = get_users(sort_by=['login'])
Jiaxi Luo31874592014-06-11 10:36:35 -07001319
1320 label_exclude_filters = [{'name__startswith': 'cros-version'},
1321 {'name__startswith': 'fw-version'}]
1322 result['labels'] = get_labels(
1323 label_exclude_filters,
1324 sort_by=['-platform', 'name'])
1325
showardc92da832009-04-07 18:14:34 +00001326 result['atomic_groups'] = get_atomic_groups(sort_by=['name'])
jadmanski0afbb632008-06-06 21:10:57 +00001327 result['tests'] = get_tests(sort_by=['name'])
showard2b9a88b2008-06-13 20:55:03 +00001328 result['profilers'] = get_profilers(sort_by=['name'])
showard0fc38302008-10-23 00:44:07 +00001329 result['current_user'] = rpc_utils.prepare_for_serialization(
showard64a95952010-01-13 21:27:16 +00001330 models.User.current_user().get_object_dict())
showard2b9a88b2008-06-13 20:55:03 +00001331 result['host_statuses'] = sorted(models.Host.Status.names)
mbligh5a198b92008-12-11 19:33:29 +00001332 result['job_statuses'] = sorted(models.HostQueueEntry.Status.names)
Simran Basi7e605742013-11-12 13:43:36 -08001333 result['job_timeout_mins_default'] = models.Job.DEFAULT_TIMEOUT_MINS
Simran Basi34217022012-11-06 13:43:15 -08001334 result['job_max_runtime_mins_default'] = (
1335 models.Job.DEFAULT_MAX_RUNTIME_MINS)
showarda1e74b32009-05-12 17:32:04 +00001336 result['parse_failed_repair_default'] = bool(
1337 models.Job.DEFAULT_PARSE_FAILED_REPAIR)
jamesrendd855242010-03-02 22:23:44 +00001338 result['reboot_before_options'] = model_attributes.RebootBefore.names
1339 result['reboot_after_options'] = model_attributes.RebootAfter.names
showard8fbae652009-01-20 23:23:10 +00001340 result['motd'] = rpc_utils.get_motd()
jamesren76fcf192010-04-21 20:39:50 +00001341 result['drone_sets_enabled'] = models.DroneSet.drone_sets_enabled()
1342 result['drone_sets'] = drone_sets
jamesren4a41e012010-07-16 22:33:48 +00001343 result['parameterized_jobs'] = models.Job.parameterized_jobs_enabled()
showard8ac29b42008-07-17 17:01:55 +00001344
showardd3dc1992009-04-22 21:01:40 +00001345 result['status_dictionary'] = {"Aborted": "Aborted",
showard8ac29b42008-07-17 17:01:55 +00001346 "Verifying": "Verifying Host",
Alex Millerdfff2fd2013-05-28 13:05:06 -07001347 "Provisioning": "Provisioning Host",
showard8ac29b42008-07-17 17:01:55 +00001348 "Pending": "Waiting on other hosts",
1349 "Running": "Running autoserv",
1350 "Completed": "Autoserv completed",
1351 "Failed": "Failed to complete",
showardd823b362008-07-24 16:35:46 +00001352 "Queued": "Queued",
showard5deb6772008-11-04 21:54:33 +00001353 "Starting": "Next in host's queue",
1354 "Stopped": "Other host(s) failed verify",
showardd3dc1992009-04-22 21:01:40 +00001355 "Parsing": "Awaiting parse of final results",
showard29f7cd22009-04-29 21:16:24 +00001356 "Gathering": "Gathering log files",
showard8cc058f2009-09-08 16:26:33 +00001357 "Template": "Template job for recurring run",
mbligh4608b002010-01-05 18:22:35 +00001358 "Waiting": "Waiting for scheduler action",
Dan Shi07e09af2013-04-12 09:31:29 -07001359 "Archiving": "Archiving results",
1360 "Resetting": "Resetting hosts"}
Jiaxi Luo421608e2014-07-07 14:38:00 -07001361
1362 result['wmatrix_url'] = rpc_utils.get_wmatrix_url()
Simran Basi71206ef2014-08-13 13:51:18 -07001363 result['is_moblab'] = bool(utils.is_moblab())
Jiaxi Luo421608e2014-07-07 14:38:00 -07001364
jadmanski0afbb632008-06-06 21:10:57 +00001365 return result
showard29f7cd22009-04-29 21:16:24 +00001366
1367
1368def get_server_time():
1369 return datetime.datetime.now().strftime("%Y-%m-%d %H:%M")