blob: 2574580d07621e3ade9426c4463c04fc0017d01f [file] [log] [blame]
Xixuan Wu0c76d5b2017-08-30 16:40:17 -07001# Copyright 2017 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""Module for tasks triggered by suite scheduler."""
Xixuan Wu244e0ec2018-05-23 14:49:55 -07006# pylint: disable=dangerous-default-value
Xixuan Wu0c76d5b2017-08-30 16:40:17 -07007
8from distutils import version
Xixuan Wu0c76d5b2017-08-30 16:40:17 -07009import logging
Xinan Lindf0698a2020-02-05 22:38:11 -080010import uuid
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070011
Xinan Lindf0698a2020-02-05 22:38:11 -080012import analytics
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070013import build_lib
14import task_executor
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070015import tot_manager
16
17# The max lifetime of a suite scheduled by suite scheduler
18_JOB_MAX_RUNTIME_MINS_DEFAULT = 72 * 60
19
20# One kind of formats for RO firmware spec.
21_RELEASE_RO_FIRMWARE_SPEC = 'released_ro_'
22
23
24class SchedulingError(Exception):
25 """Raised to indicate a failure in scheduling a task."""
26
27
28class Task(object):
29 """Represents an entry from the suite_scheduler config file.
30
31 Each entry from the suite_scheduler config file maps one-to-one to a
32 Task. Each instance has enough information to schedule itself.
33 """
34
Xinan Lin33937d62020-04-14 14:41:23 -070035 def __init__(self,
36 task_info,
37 board_family_config={},
38 tot=None,
39 is_sanity=False):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070040 """Initialize a task instance.
41
42 Args:
43 task_info: a config_reader.TaskInfo object, which includes:
44 name, name of this task, e.g. 'NightlyPower'
45 suite, the name of the suite to run, e.g. 'graphics_per-day'
46 branch_specs, a pre-vetted iterable of branch specifiers,
47 e.g. ['>=R18', 'factory']
48 pool, the pool of machines to schedule tasks. Default is None.
49 num, the number of devices to shard the test suite. It could
50 be an Integer or None. By default it's None.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070051 board_families, a common separated list of board family to run this
52 task on. Boards belong to one of the board family in this list
53 would be added to task_info.boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070054 boards, a comma separated list of boards to run this task on. Default
Po-Hsien Wangdd833072018-08-16 18:09:20 -070055 is None, which allows this task to run on all boards. If same board
56 is specified in 'boards' and 'exclude_boards', we exclude this
57 board.
Xinan Linc8647112020-02-04 16:45:56 -080058 dimensions, a comma separated lists of labels. Each label is in
59 the form of 'key:value'.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070060 exclude_board_families, a common separated list of board family not to
61 run task on. Boards belong to one of the board family in this list
62 would be added to task_info.exclude_boards.
Po-Hsien Wang6d589732018-05-15 17:19:34 -070063 exclude_boards, a comma separated list of boards not to run this task
64 on. Default is None, which allows this task to run on all boards.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070065 If same board is specified in 'boards' and 'exclude_boards', we
66 exclude this board.
Xixuan Wu89897182019-01-03 15:28:01 -080067 models, a comma separated list of models to run this task on. Default
68 is None, which allows this task to run on all models. If same model
69 is specified in 'models' and 'exclude_models', we exclude this
70 model.
71 exclude_models, a comma separated list of models not to run this task
72 on. Default is None, which allows this task to run on all models.
C Shapiro09108252019-08-01 14:52:52 -050073 any_model, set to True to not pass the model parameter and allow
74 a test suite to run any/all models available for testing.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070075 priority, the string name of a priority from constants.Priorities.
76 timeout, the max lifetime of the suite in hours.
77 cros_build_spec, spec used to determine the ChromeOS build to test
78 with a firmware build, e.g., tot, R41 etc.
79 firmware_rw_build_spec, spec used to determine the firmware RW build
80 test with a ChromeOS build.
81 firmware_ro_build_spec, spec used to determine the firmware RO build
82 test with a ChromeOS build.
83 test_source, the source of test code when firmware will be updated in
84 the test. The value can be 'firmware_rw', 'firmware_ro' or 'cros'.
85 job_retry, set to True to enable job-level retry. Default is False.
Xixuan Wu80531932017-10-12 17:26:51 -070086 no_delay, set to True to raise the priority of this task in task.
87 force, set to True to schedule this suite no matter whether there's
88 duplicate jobs before.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070089 queue, so the suite jobs can start running tests with no waiting.
90 hour, an integer specifying the hour that a nightly run should be
91 triggered, default is set to 21.
92 day, an integer specifying the day of a week that a weekly run should
93 be triggered, default is set to 5 (Saturday).
94 os_type, type of OS, e.g., cros, brillo, android. Default is cros.
95 The argument is required for android/brillo builds.
96 launch_control_branches, comma separated string of launch control
97 branches. The argument is required and only applicable for
98 android/brillo builds.
99 launch_control_targets, comma separated string of build targets for
100 launch control builds. The argument is required and only
101 applicable for android/brillo builds.
102 testbed_dut_count, number of duts to test when using a testbed.
Xinan Lin4757d6f2020-03-24 22:20:31 -0700103 qs_account, quota account for the unmanaged pool which has enabled
104 Quota Scheduler.
Xixuan Wu83118dd2018-08-27 12:11:35 -0700105 board_family_config: A board family dictionary mapping board_family name
106 to its corresponding boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700107 tot: The tot manager for checking ToT. If it's None, a new tot_manager
108 instance will be initialized.
Xinan Lin33937d62020-04-14 14:41:23 -0700109 is_sanity: A boolean; true if we are running in sanity env.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700110 """
Xixuan Wu5451a662017-10-17 10:57:40 -0700111 # Indicate whether there're suites get pushed into taskqueue for this task.
112 self.is_pushed = False
113
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700114 self.name = task_info.name
115 self.suite = task_info.suite
116 self.branch_specs = task_info.branch_specs
117 self.pool = task_info.pool
118 self.num = task_info.num
119 self.priority = task_info.priority
120 self.timeout = task_info.timeout
121 self.cros_build_spec = task_info.cros_build_spec
122 self.firmware_rw_build_spec = task_info.firmware_rw_build_spec
123 self.firmware_ro_build_spec = task_info.firmware_ro_build_spec
124 self.test_source = task_info.test_source
125 self.job_retry = task_info.job_retry
126 self.no_delay = task_info.no_delay
Xixuan Wu80531932017-10-12 17:26:51 -0700127 self.force = task_info.force
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700128 self.os_type = task_info.os_type
Xinan Lin3ba18a02019-08-13 15:44:55 -0700129 self.frontdoor = task_info.frontdoor
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700130 self.testbed_dut_count = task_info.testbed_dut_count
Xixuan Wu008ee832017-10-12 16:59:34 -0700131 self.hour = task_info.hour
132 self.day = task_info.day
Craig Bergstrom58263d32018-04-26 14:11:35 -0600133 self.only_hwtest_sanity_required = task_info.only_hwtest_sanity_required
Xinan Lin4757d6f2020-03-24 22:20:31 -0700134 self.qs_account = task_info.qs_account
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700135
136 if task_info.lc_branches:
137 self.launch_control_branches = [
138 t.strip() for t in task_info.lc_branches.split(',')]
139 else:
140 self.launch_control_branches = []
141
142 if task_info.lc_targets:
143 self.launch_control_targets = [
144 t.strip() for t in task_info.lc_targets.split(',')]
145 else:
146 self.launch_control_targets = []
147
148 if task_info.boards:
149 self.boards = [t.strip() for t in task_info.boards.split(',')]
150 else:
151 self.boards = []
152
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700153 if task_info.exclude_boards:
154 self.exclude_boards = [
155 t.strip() for t in task_info.exclude_boards.split(',')]
156 else:
157 self.exclude_boards = []
158
Xixuan Wu89897182019-01-03 15:28:01 -0800159 if task_info.models:
160 self.models = [t.strip() for t in task_info.models.split(',')]
161 else:
162 self.models = []
163
164 if task_info.exclude_models:
165 self.exclude_models = [
166 t.strip() for t in task_info.exclude_models.split(',')]
167 else:
168 self.exclude_models = []
169
C Shapiro09108252019-08-01 14:52:52 -0500170 self.any_model = task_info.any_model
171
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700172 if task_info.board_families:
173 # Finetune the allowed boards list with board_families & boards.
174 families = [family.strip()
175 for family in task_info.board_families.split(',')]
176 for family in families:
177 self.boards += board_family_config.get(family, [])
Xixuan Wu89897182019-01-03 15:28:01 -0800178
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700179 if task_info.exclude_board_families:
180 # Finetune the disallowed boards list with exclude_board_families
181 # & exclude_boards.
182 families = [family.strip()
183 for family in task_info.exclude_board_families.split(',')]
184 for family in families:
185 self.exclude_boards += board_family_config.get(family, [])
186
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700187 if tot is None:
188 self.tot_manager = tot_manager.TotMilestoneManager()
189 else:
190 self.tot_manager = tot
191
Xinan Linc8647112020-02-04 16:45:56 -0800192 self.dimensions = task_info.dimensions
193
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700194 self._set_spec_compare_info()
Xinan Lin33937d62020-04-14 14:41:23 -0700195 # Sanity test does not have to upload metrics.
196 if not is_sanity:
197 self.job_section = analytics.ScheduleJobSection(task_info)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700198
Xinan Lin028f9582019-12-11 10:55:33 -0800199 def schedule(self, launch_control_builds, cros_builds_tuple,
200 firmware_builds, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700201 """Schedule the task by its settings.
202
203 Args:
204 launch_control_builds: the build dict for Android boards, see
205 return value of |get_launch_control_builds|.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600206 cros_builds_tuple: the two-tuple of build dicts for ChromeOS boards,
207 see return value of |get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800208 firmware_builds: a dict of firmware artifact, see return value of
209 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700210 configs: a config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700211
212 Raises:
213 SchedulingError: if tasks that should be scheduled fail to schedule.
Xixuan Wu5451a662017-10-17 10:57:40 -0700214
215 Returns:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700216 A boolean indicator; true if there were any suites related to this
217 task which got pushed into the suites queue.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700218 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700219 assert configs.lab_config is not None
Xixuan Wu5451a662017-10-17 10:57:40 -0700220 self.is_pushed = False
221
Craig Bergstrom58263d32018-04-26 14:11:35 -0600222 branch_builds, relaxed_builds = cros_builds_tuple
223 builds_dict = branch_builds
224 if self.only_hwtest_sanity_required:
Xinan Linae7d6372019-09-12 14:42:10 -0700225 builds_dict = _split_unibuilds(relaxed_builds, configs)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600226
Xinan Lindf0698a2020-02-05 22:38:11 -0800227 # Record all target boards and models into job section.
228 lab_config = configs.lab_config
229 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
230 boards = self.boards if self.boards else lab_config.get_cros_board_list()
231 for b in boards:
232 if self.exclude_boards and b in self.exclude_boards:
233 continue
234 self.job_section.add_board(b)
235 models = self.models or models_by_board.get(b, [])
236 for m in models:
237 if m and '%s_%s' % (b, m) not in self.exclude_models:
238 self.job_section.add_model(m)
239
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700240 logging.info('######## Scheduling task %s ########', self.name)
241 if self.os_type == build_lib.OS_TYPE_CROS:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600242 if not builds_dict:
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700243 logging.info('No CrOS build to run, skip running.')
244 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800245 self._schedule_cros_builds(builds_dict, firmware_builds, configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700246 else:
247 if not launch_control_builds:
248 logging.info('No Android build to run, skip running.')
249 else:
250 self._schedule_launch_control_builds(launch_control_builds)
Xinan Lindf0698a2020-02-05 22:38:11 -0800251 upload_result = False
252 try:
253 upload_result = self.job_section.upload()
254 # For any exceptions from BQ, only log it and move on.
255 except Exception as e: #pylint: disable=broad-except
256 logging.exception(str(e))
257 if not upload_result:
258 logging.warning('Failed to insert row: %r', self.job_section)
Xixuan Wu5451a662017-10-17 10:57:40 -0700259 return self.is_pushed
260
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700261 def _set_spec_compare_info(self):
262 """Set branch spec compare info for task for further check."""
263 self._bare_branches = []
264 self._version_equal_constraint = False
265 self._version_gte_constraint = False
266 self._version_lte_constraint = False
267
268 if not self.branch_specs:
269 # Any milestone is OK.
270 self._numeric_constraint = version.LooseVersion('0')
271 else:
272 self._numeric_constraint = None
273 for spec in self.branch_specs:
274 if 'tot' in spec.lower():
275 # Convert spec >=tot-1 to >=RXX.
276 tot_str = spec[spec.index('tot'):]
277 spec = spec.replace(
278 tot_str, self.tot_manager.convert_tot_spec(tot_str))
279
280 if spec.startswith('>='):
281 self._numeric_constraint = version.LooseVersion(
282 spec.lstrip('>=R'))
283 self._version_gte_constraint = True
284 elif spec.startswith('<='):
285 self._numeric_constraint = version.LooseVersion(
286 spec.lstrip('<=R'))
287 self._version_lte_constraint = True
288 elif spec.startswith('=='):
289 self._version_equal_constraint = True
290 self._numeric_constraint = version.LooseVersion(
291 spec.lstrip('==R'))
292 else:
293 self._bare_branches.append(spec)
294
295 def _fits_spec(self, branch):
296 """Check if a branch is deemed OK by this task's branch specs.
297
298 Will return whether a branch 'fits' the specifications stored in this task.
299
300 Examples:
301 Assuming tot=R40
302 t = Task('Name', 'suite', ['factory', '>=tot-1'])
303 t._fits_spec('factory') # True
304 t._fits_spec('40') # True
305 t._fits_spec('38') # False
306 t._fits_spec('firmware') # False
307
308 Args:
309 branch: the branch to check.
310
311 Returns:
312 True if branch 'fits' with stored specs, False otherwise.
313 """
314 if branch in build_lib.BARE_BRANCHES:
315 return branch in self._bare_branches
316
317 if self._numeric_constraint:
318 if self._version_equal_constraint:
319 return version.LooseVersion(branch) == self._numeric_constraint
320 elif self._version_gte_constraint:
321 return version.LooseVersion(branch) >= self._numeric_constraint
322 elif self._version_lte_constraint:
323 return version.LooseVersion(branch) <= self._numeric_constraint
324 else:
325 return version.LooseVersion(branch) >= self._numeric_constraint
326 else:
327 return False
328
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700329 def _get_latest_firmware_build_from_lab_config(self, spec, board,
330 lab_config):
331 """Get latest firmware from lab config file.
332
333 Args:
334 spec: a string build spec for RO or RW firmware, eg. firmware,
335 cros. For RO firmware, the value can also be released_ro_X.
336 board: a string board against which this task will run suite job.
337 lab_config: a config.LabConfig object, to read lab config file.
338
339 Returns:
340 A string latest firmware build.
341
342 Raises:
343 ValueError: if no firmware build list is found in lab config file.
344 """
345 index = int(spec[len(_RELEASE_RO_FIRMWARE_SPEC):])
346 released_ro_builds = lab_config.get_firmware_ro_build_list(
347 'RELEASED_RO_BUILDS_%s' % board).split(',')
348 if len(released_ro_builds) < index:
349 raise ValueError('No %dth ro_builds in the lab_config firmware '
350 'list %r' % (index, released_ro_builds))
351
352 logging.debug('Get ro_build: %s', released_ro_builds[index - 1])
353 return released_ro_builds[index - 1]
354
Xinan Lin028f9582019-12-11 10:55:33 -0800355 def _get_firmware_build(self, spec, board, firmware_build_dict, lab_config):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700356 """Get the firmware build name to test with ChromeOS build.
357
358 Args:
359 spec: a string build spec for RO or RW firmware, eg. firmware,
360 cros. For RO firmware, the value can also be released_ro_X.
361 board: a string board against which this task will run suite job.
Xinan Lin028f9582019-12-11 10:55:33 -0800362 firmware_build_dict: a dict of firmware artifacts, see return value of
363 |base_event.get_firmware_builds|.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700364 lab_config: a config.LabConfig object, to read lab config file.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700365
366 Returns:
367 A string firmware build name.
368
369 Raises:
370 ValueError: if failing to get firmware from lab config file;
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700371 """
372 if not spec or spec == 'stable':
373 # TODO(crbug.com/577316): Query stable RO firmware.
374 logging.debug('%s RO firmware build is not supported.', spec)
375 return None
376
377 try:
378 if spec.startswith(_RELEASE_RO_FIRMWARE_SPEC):
379 # For RO firmware whose value is released_ro_X, where X is the index of
380 # the list defined in lab config file:
381 # CROS/RELEASED_RO_BUILDS_[board].
382 # For example, for spec 'released_ro_2', and lab config file
383 # CROS/RELEASED_RO_BUILDS_veyron_jerry: build1,build2,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600384 # return firmware RO build should be 'build2'.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700385 return self._get_latest_firmware_build_from_lab_config(
386 spec, board, lab_config)
Xinan Lin028f9582019-12-11 10:55:33 -0800387 elif firmware_build_dict:
388 return firmware_build_dict.get((spec, board), None)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700389 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800390 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700391 except ValueError as e:
392 logging.warning('Failed to get firmware from lab config file'
393 'for spec %s, board %s: %s', spec, board, str(e))
394 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700395
C Shapiro7f24a002017-12-05 14:25:09 -0700396 def _push_suite(
397 self,
Xinan Lindf0698a2020-02-05 22:38:11 -0800398 task_id=None,
C Shapiro7f24a002017-12-05 14:25:09 -0700399 board=None,
400 model=None,
401 cros_build=None,
402 firmware_rw_build=None,
403 firmware_ro_build=None,
404 test_source_build=None,
405 launch_control_build=None,
Xinan Lin4757d6f2020-03-24 22:20:31 -0700406 run_prod_code=False):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700407 """Schedule suite job for the task by pushing suites to SuiteQueue.
408
409 Args:
Xinan Lindf0698a2020-02-05 22:38:11 -0800410 task_id: the id to track this task in exectuion.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700411 board: the board against which this suite job run.
C Shapiro7f24a002017-12-05 14:25:09 -0700412 model: the model name for unibuild.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700413 cros_build: the CrOS build of this suite job.
414 firmware_rw_build: Firmware RW build to run this suite job with.
415 firmware_ro_build: Firmware RO build to run this suite job with.
416 test_source_build: Test source build, used for server-side
417 packaging of this suite job.
418 launch_control_build: the launch control build of this suite job.
419 run_prod_code: If True, the suite will run the test code that lives
420 in prod aka the test code currently on the lab servers. If
421 False, the control files and test code for this suite run will
422 be retrieved from the build artifacts. Default is False.
423 """
424 android_build = None
425 testbed_build = None
426
427 if self.testbed_dut_count:
428 launch_control_build = '%s#%d' % (launch_control_build,
429 self.testbed_dut_count)
430 test_source_build = launch_control_build
431 board = '%s-%d' % (board, self.testbed_dut_count)
432
433 if launch_control_build:
434 if not self.testbed_dut_count:
435 android_build = launch_control_build
436 else:
437 testbed_build = launch_control_build
438
439 suite_job_parameters = {
Xinan Linba3b9322020-04-24 15:08:12 -0700440 'name': self.name,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700441 'suite': self.suite,
Xinan Lindf0698a2020-02-05 22:38:11 -0800442 'task_id': task_id,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700443 'board': board,
C Shapiro7f24a002017-12-05 14:25:09 -0700444 'model': model,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700445 build_lib.BuildVersionKey.CROS_VERSION: cros_build,
446 build_lib.BuildVersionKey.FW_RW_VERSION: firmware_rw_build,
447 build_lib.BuildVersionKey.FW_RO_VERSION: firmware_ro_build,
448 build_lib.BuildVersionKey.ANDROID_BUILD_VERSION: android_build,
449 build_lib.BuildVersionKey.TESTBED_BUILD_VERSION: testbed_build,
450 'num': self.num,
451 'pool': self.pool,
452 'priority': self.priority,
453 'timeout': self.timeout,
454 'timeout_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
455 'max_runtime_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
Xixuan Wu2ba72652017-09-15 15:49:42 -0700456 'no_wait_for_results': not self.job_retry,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700457 'test_source_build': test_source_build,
458 'job_retry': self.job_retry,
459 'no_delay': self.no_delay,
Xixuan Wu80531932017-10-12 17:26:51 -0700460 'force': self.force,
Xinan Linc8647112020-02-04 16:45:56 -0800461 'dimensions': self.dimensions,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700462 'run_prod_code': run_prod_code,
Xinan Lin4757d6f2020-03-24 22:20:31 -0700463 'qs_account': self.qs_account,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700464 }
465
Xinan Lin9e4917d2019-11-04 10:58:47 -0800466 task_executor.push(task_executor.SUITES_QUEUE,
467 tag=self.suite,
468 **suite_job_parameters)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700469 logging.info('Pushing task %r into taskqueue', suite_job_parameters)
Xixuan Wu5451a662017-10-17 10:57:40 -0700470 self.is_pushed = True
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700471
Xinan Lin028f9582019-12-11 10:55:33 -0800472 def _schedule_cros_builds(self, build_dict, firmware_build_dict, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700473 """Schedule tasks with branch builds.
474
475 Args:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600476 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800477 value of |build_lib.get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800478 firmware_build_dict: a dict of firmware artifact, see return value of
479 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700480 configs: A config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700481 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700482 lab_config = configs.lab_config
C Shapiro7f24a002017-12-05 14:25:09 -0700483 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
C Shapiro09108252019-08-01 14:52:52 -0500484 model_agnostic_cros_builds = set()
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700485 for (board, passed_model, build_type,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600486 milestone), manifest in build_dict.iteritems():
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700487 cros_build = str(build_lib.CrOSBuild(board, build_type, milestone,
488 manifest))
489 logging.info('Running %s on %s', self.name, cros_build)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700490 if self.exclude_boards and board in self.exclude_boards:
491 logging.debug('Board %s is in excluded board list: %s',
492 board, self.exclude_boards)
493 continue
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700494
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700495 if self.boards and board not in self.boards:
496 logging.debug('Board %s is not in supported board list: %s',
497 board, self.boards)
498 continue
499
500 # Check the fitness of the build's branch for task
501 branch_build_spec = _pick_branch(build_type, milestone)
502 if not self._fits_spec(branch_build_spec):
Xinan Lindf0698a2020-02-05 22:38:11 -0800503 msg = ("branch_build spec %s doesn't fit this task's "
504 "requirement: %s") % (branch_build_spec,
505 ",".join(self.branch_specs))
506 logging.debug(msg)
507 self.job_section.add_schedule_job(board, passed_model, msg=msg)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700508 continue
509
Xinan Lindf0698a2020-02-05 22:38:11 -0800510 # Record this build as it matches both board and branch specs.
511 if self.only_hwtest_sanity_required:
512 self.job_section.add_matched_relax_build(
513 board, build_type, milestone, manifest)
514 else:
515 self.job_section.add_matched_build(
516 board, build_type, milestone, manifest)
517
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700518 firmware_rw_build = None
519 firmware_ro_build = None
520 if self.firmware_rw_build_spec or self.firmware_ro_build_spec:
521 firmware_rw_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800522 self.firmware_rw_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700523 firmware_ro_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800524 self.firmware_ro_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700525
526 if not firmware_ro_build and self.firmware_ro_build_spec:
Xinan Lindf0698a2020-02-05 22:38:11 -0800527 msg = 'No RO firmware ro build to run, skip running'
528 logging.debug(msg)
529 self.job_section.add_schedule_job(board, passed_model, msg=msg)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700530 continue
531
532 if self.test_source == build_lib.BuildType.FIRMWARE_RW:
533 test_source_build = firmware_rw_build
534 else:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700535 # Default test source build to CrOS build if it's not specified.
536 # Past versions chose based on run_prod_code, but we no longer respect
537 # that option and scheduler settings should always set it to False.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700538 test_source_build = cros_build
539
Xinan Lindf0698a2020-02-05 22:38:11 -0800540 # Record the matched firwmare build.
541 if firmware_rw_build:
542 self.job_section.add_matched_fw_build(
543 board,
544 self.firmware_rw_build_spec,
545 firmware_rw_build,
546 read_only=False)
547 if firmware_ro_build:
548 self.job_section.add_matched_fw_build(
549 board,
550 self.firmware_ro_build_spec,
551 firmware_ro_build,
552 read_only=True)
553
Xinan Lin6668e0f2020-05-29 10:02:57 -0700554 # Board above is used as build target to control the CrOS image.
555 # The following part is to assign models for lab boards, where
556 # the suffix should be removed.
Xixuan Wub4b2f412019-05-03 11:22:31 -0700557 hwtest_board = build_lib.reshape_board(board)
Xinan Lin0550f492020-01-21 16:25:53 -0800558
Xinan Lin6668e0f2020-05-29 10:02:57 -0700559 models = models_by_board.get(hwtest_board, [None])
C Shapiro7f24a002017-12-05 14:25:09 -0700560
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000561 for model in models:
562 if ((passed_model is not None and model == passed_model) or
563 passed_model is None):
Xinan Lin6668e0f2020-05-29 10:02:57 -0700564 full_model_name = '%s_%s' % (hwtest_board, model)
Xixuan Wua41efa22019-05-17 14:28:04 -0700565 # Respect exclude first.
566 if self.exclude_models and full_model_name in self.exclude_models:
567 logging.debug("Skip model %s as it's in exclude model list %s",
568 model, self.exclude_models)
569 continue
570
571 if self.models and full_model_name not in self.models:
572 logging.debug("Skip model %s as it's not in support model list %s",
573 model, self.models)
574 continue
575
C Shapiro09108252019-08-01 14:52:52 -0500576 explicit_model = model
577
578 if self.any_model:
Xinan Lin3ba18a02019-08-13 15:44:55 -0700579 explicit_model = None
C Shapiro09108252019-08-01 14:52:52 -0500580 unique_build = str(cros_build)
581 if unique_build in model_agnostic_cros_builds:
582 # Skip since we've already run with no explicit model set.
Xinan Lindf0698a2020-02-05 22:38:11 -0800583 msg = "Skip model %s as any_model enabled for this job." % model
584 self.job_section.add_schedule_job(board, model, msg=msg)
C Shapiro09108252019-08-01 14:52:52 -0500585 continue
586 model_agnostic_cros_builds.add(unique_build)
587
Xinan Lindf0698a2020-02-05 22:38:11 -0800588 task_id = str(uuid.uuid1())
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000589 self._push_suite(
Xinan Lindf0698a2020-02-05 22:38:11 -0800590 task_id=task_id,
Xixuan Wub4b2f412019-05-03 11:22:31 -0700591 board=hwtest_board,
C Shapiro09108252019-08-01 14:52:52 -0500592 model=explicit_model,
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000593 cros_build=cros_build,
594 firmware_rw_build=firmware_rw_build,
595 firmware_ro_build=firmware_ro_build,
Xinan Lin0550f492020-01-21 16:25:53 -0800596 test_source_build=test_source_build)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700597
Xinan Lin6668e0f2020-05-29 10:02:57 -0700598 # Analytics table stores the build target instead of the lab board.
Xinan Lin0673a182020-04-14 15:09:35 -0700599 self.job_section.add_schedule_job(
600 board, explicit_model, task_id=task_id)
Xinan Lindf0698a2020-02-05 22:38:11 -0800601
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700602 def _schedule_launch_control_builds(self, launch_control_builds):
603 """Schedule tasks with launch control builds.
604
605 Args:
606 launch_control_builds: the build dict for Android boards.
607 """
608 for board, launch_control_build in launch_control_builds.iteritems():
609 logging.debug('Running %s on %s', self.name, board)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700610 if self.exclude_boards and board in self.exclude_boards:
611 logging.debug('Board %s is in excluded board list: %s',
612 board, self.exclude_boards)
613 continue
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700614 if self.boards and board not in self.boards:
615 logging.debug('Board %s is not in supported board list: %s',
616 board, self.boards)
617 continue
618
619 for android_build in launch_control_build:
620 if not any([branch in android_build
621 for branch in self.launch_control_branches]):
622 logging.debug('Branch %s is not required to run for task '
623 '%s', android_build, self.name)
624 continue
625
626 self._push_suite(board=board,
627 test_source_build=android_build,
628 launch_control_build=android_build)
629
630
631def _pick_branch(build_type, milestone):
632 """Select branch based on build type.
633
634 If the build_type is a bare branch, return build_type as the build spec.
635 If the build_type is a normal CrOS branch, return milestone as the build
636 spec.
637
638 Args:
639 build_type: a string builder name, like 'release'.
640 milestone: a string milestone, like '55'.
641
642 Returns:
643 A string milestone if build_type represents CrOS build, otherwise
644 return build_type.
645 """
646 return build_type if build_type in build_lib.BARE_BRANCHES else milestone
Xinan Linae7d6372019-09-12 14:42:10 -0700647
648
649def _split_unibuilds(build_dict, configs):
650 """Split the uni-builds to all models under a board.
651
652 Args:
653 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800654 value of |build_lib.get_cros_builds|.
Xinan Linae7d6372019-09-12 14:42:10 -0700655 configs: a config_reader.Configs object.
656
657 Returns:
658 A build dict.
659 """
660 models_by_board = configs.lab_config.get_cros_model_map()
661 if not models_by_board:
662 return build_dict
663 all_branch_build_dict = {}
664 for (board, model, config, milestone), platform in build_dict.iteritems():
665 uni_build_models = models_by_board.get(board)
666 if uni_build_models is not None and model is None:
667 for uni_build_model in uni_build_models:
668 model_key = (board, uni_build_model, config, milestone)
669 _add_build_dict(all_branch_build_dict, model_key, platform)
670 continue
671 build_key = (board, model, config, milestone)
672 _add_build_dict(all_branch_build_dict, build_key, platform)
673
674 return all_branch_build_dict
675
676
677def _add_build_dict(build_dict, key, value):
678 """A wrapper to add or update an item in build_dict."""
679 cur_manifest = build_dict.get(key)
680 if cur_manifest is None:
681 build_dict[key] = value
682 return
683 build_dict[key] = max(
684 [cur_manifest, value], key=version.LooseVersion)