blob: e11705bc0b862baaff5b5ded3941d0683d094af4 [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
10
11import build_lib
12import task_executor
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070013import tot_manager
14
15# The max lifetime of a suite scheduled by suite scheduler
16_JOB_MAX_RUNTIME_MINS_DEFAULT = 72 * 60
17
18# One kind of formats for RO firmware spec.
19_RELEASE_RO_FIRMWARE_SPEC = 'released_ro_'
20
21
22class SchedulingError(Exception):
23 """Raised to indicate a failure in scheduling a task."""
24
25
26class Task(object):
27 """Represents an entry from the suite_scheduler config file.
28
29 Each entry from the suite_scheduler config file maps one-to-one to a
30 Task. Each instance has enough information to schedule itself.
31 """
32
Po-Hsien Wangdd833072018-08-16 18:09:20 -070033 def __init__(self, task_info, board_family_config={}, tot=None):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070034 """Initialize a task instance.
35
36 Args:
37 task_info: a config_reader.TaskInfo object, which includes:
38 name, name of this task, e.g. 'NightlyPower'
39 suite, the name of the suite to run, e.g. 'graphics_per-day'
40 branch_specs, a pre-vetted iterable of branch specifiers,
41 e.g. ['>=R18', 'factory']
42 pool, the pool of machines to schedule tasks. Default is None.
43 num, the number of devices to shard the test suite. It could
44 be an Integer or None. By default it's None.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070045 board_families, a common separated list of board family to run this
46 task on. Boards belong to one of the board family in this list
47 would be added to task_info.boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070048 boards, a comma separated list of boards to run this task on. Default
Po-Hsien Wangdd833072018-08-16 18:09:20 -070049 is None, which allows this task to run on all boards. If same board
50 is specified in 'boards' and 'exclude_boards', we exclude this
51 board.
52 exclude_board_families, a common separated list of board family not to
53 run task on. Boards belong to one of the board family in this list
54 would be added to task_info.exclude_boards.
Po-Hsien Wang6d589732018-05-15 17:19:34 -070055 exclude_boards, a comma separated list of boards not to run this task
56 on. Default is None, which allows this task to run on all boards.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070057 If same board is specified in 'boards' and 'exclude_boards', we
58 exclude this board.
Xixuan Wu89897182019-01-03 15:28:01 -080059 models, a comma separated list of models to run this task on. Default
60 is None, which allows this task to run on all models. If same model
61 is specified in 'models' and 'exclude_models', we exclude this
62 model.
63 exclude_models, a comma separated list of models not to run this task
64 on. Default is None, which allows this task to run on all models.
C Shapiro09108252019-08-01 14:52:52 -050065 any_model, set to True to not pass the model parameter and allow
66 a test suite to run any/all models available for testing.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070067 priority, the string name of a priority from constants.Priorities.
68 timeout, the max lifetime of the suite in hours.
69 cros_build_spec, spec used to determine the ChromeOS build to test
70 with a firmware build, e.g., tot, R41 etc.
71 firmware_rw_build_spec, spec used to determine the firmware RW build
72 test with a ChromeOS build.
73 firmware_ro_build_spec, spec used to determine the firmware RO build
74 test with a ChromeOS build.
75 test_source, the source of test code when firmware will be updated in
76 the test. The value can be 'firmware_rw', 'firmware_ro' or 'cros'.
77 job_retry, set to True to enable job-level retry. Default is False.
Xixuan Wu80531932017-10-12 17:26:51 -070078 no_delay, set to True to raise the priority of this task in task.
79 force, set to True to schedule this suite no matter whether there's
80 duplicate jobs before.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070081 queue, so the suite jobs can start running tests with no waiting.
82 hour, an integer specifying the hour that a nightly run should be
83 triggered, default is set to 21.
84 day, an integer specifying the day of a week that a weekly run should
85 be triggered, default is set to 5 (Saturday).
86 os_type, type of OS, e.g., cros, brillo, android. Default is cros.
87 The argument is required for android/brillo builds.
88 launch_control_branches, comma separated string of launch control
89 branches. The argument is required and only applicable for
90 android/brillo builds.
91 launch_control_targets, comma separated string of build targets for
92 launch control builds. The argument is required and only
93 applicable for android/brillo builds.
94 testbed_dut_count, number of duts to test when using a testbed.
Xixuan Wu83118dd2018-08-27 12:11:35 -070095 board_family_config: A board family dictionary mapping board_family name
96 to its corresponding boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070097 tot: The tot manager for checking ToT. If it's None, a new tot_manager
98 instance will be initialized.
99 """
Xixuan Wu5451a662017-10-17 10:57:40 -0700100 # Indicate whether there're suites get pushed into taskqueue for this task.
101 self.is_pushed = False
102
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700103 self.name = task_info.name
104 self.suite = task_info.suite
105 self.branch_specs = task_info.branch_specs
106 self.pool = task_info.pool
107 self.num = task_info.num
108 self.priority = task_info.priority
109 self.timeout = task_info.timeout
110 self.cros_build_spec = task_info.cros_build_spec
111 self.firmware_rw_build_spec = task_info.firmware_rw_build_spec
112 self.firmware_ro_build_spec = task_info.firmware_ro_build_spec
113 self.test_source = task_info.test_source
114 self.job_retry = task_info.job_retry
115 self.no_delay = task_info.no_delay
Xixuan Wu80531932017-10-12 17:26:51 -0700116 self.force = task_info.force
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700117 self.os_type = task_info.os_type
Xinan Lin3ba18a02019-08-13 15:44:55 -0700118 self.frontdoor = task_info.frontdoor
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700119 self.testbed_dut_count = task_info.testbed_dut_count
Xixuan Wu008ee832017-10-12 16:59:34 -0700120 self.hour = task_info.hour
121 self.day = task_info.day
Craig Bergstrom58263d32018-04-26 14:11:35 -0600122 self.only_hwtest_sanity_required = task_info.only_hwtest_sanity_required
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700123
124 if task_info.lc_branches:
125 self.launch_control_branches = [
126 t.strip() for t in task_info.lc_branches.split(',')]
127 else:
128 self.launch_control_branches = []
129
130 if task_info.lc_targets:
131 self.launch_control_targets = [
132 t.strip() for t in task_info.lc_targets.split(',')]
133 else:
134 self.launch_control_targets = []
135
136 if task_info.boards:
137 self.boards = [t.strip() for t in task_info.boards.split(',')]
138 else:
139 self.boards = []
140
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700141 if task_info.exclude_boards:
142 self.exclude_boards = [
143 t.strip() for t in task_info.exclude_boards.split(',')]
144 else:
145 self.exclude_boards = []
146
Xixuan Wu89897182019-01-03 15:28:01 -0800147 if task_info.models:
148 self.models = [t.strip() for t in task_info.models.split(',')]
149 else:
150 self.models = []
151
152 if task_info.exclude_models:
153 self.exclude_models = [
154 t.strip() for t in task_info.exclude_models.split(',')]
155 else:
156 self.exclude_models = []
157
C Shapiro09108252019-08-01 14:52:52 -0500158 self.any_model = task_info.any_model
159
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700160 if task_info.board_families:
161 # Finetune the allowed boards list with board_families & boards.
162 families = [family.strip()
163 for family in task_info.board_families.split(',')]
164 for family in families:
165 self.boards += board_family_config.get(family, [])
Xixuan Wu89897182019-01-03 15:28:01 -0800166
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700167 if task_info.exclude_board_families:
168 # Finetune the disallowed boards list with exclude_board_families
169 # & exclude_boards.
170 families = [family.strip()
171 for family in task_info.exclude_board_families.split(',')]
172 for family in families:
173 self.exclude_boards += board_family_config.get(family, [])
174
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700175 if tot is None:
176 self.tot_manager = tot_manager.TotMilestoneManager()
177 else:
178 self.tot_manager = tot
179
180 self._set_spec_compare_info()
181
Xinan Lin028f9582019-12-11 10:55:33 -0800182 def schedule(self, launch_control_builds, cros_builds_tuple,
183 firmware_builds, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700184 """Schedule the task by its settings.
185
186 Args:
187 launch_control_builds: the build dict for Android boards, see
188 return value of |get_launch_control_builds|.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600189 cros_builds_tuple: the two-tuple of build dicts for ChromeOS boards,
190 see return value of |get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800191 firmware_builds: a dict of firmware artifact, see return value of
192 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700193 configs: a config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700194
195 Raises:
196 SchedulingError: if tasks that should be scheduled fail to schedule.
Xixuan Wu5451a662017-10-17 10:57:40 -0700197
198 Returns:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700199 A boolean indicator; true if there were any suites related to this
200 task which got pushed into the suites queue.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700201 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700202 assert configs.lab_config is not None
Xixuan Wu5451a662017-10-17 10:57:40 -0700203 self.is_pushed = False
204
Craig Bergstrom58263d32018-04-26 14:11:35 -0600205 branch_builds, relaxed_builds = cros_builds_tuple
206 builds_dict = branch_builds
207 if self.only_hwtest_sanity_required:
Xinan Linae7d6372019-09-12 14:42:10 -0700208 builds_dict = _split_unibuilds(relaxed_builds, configs)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600209
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700210 logging.info('######## Scheduling task %s ########', self.name)
211 if self.os_type == build_lib.OS_TYPE_CROS:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600212 if not builds_dict:
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700213 logging.info('No CrOS build to run, skip running.')
214 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800215 self._schedule_cros_builds(builds_dict, firmware_builds, configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700216 else:
217 if not launch_control_builds:
218 logging.info('No Android build to run, skip running.')
219 else:
220 self._schedule_launch_control_builds(launch_control_builds)
221
Xixuan Wu5451a662017-10-17 10:57:40 -0700222 return self.is_pushed
223
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700224 def _set_spec_compare_info(self):
225 """Set branch spec compare info for task for further check."""
226 self._bare_branches = []
227 self._version_equal_constraint = False
228 self._version_gte_constraint = False
229 self._version_lte_constraint = False
230
231 if not self.branch_specs:
232 # Any milestone is OK.
233 self._numeric_constraint = version.LooseVersion('0')
234 else:
235 self._numeric_constraint = None
236 for spec in self.branch_specs:
237 if 'tot' in spec.lower():
238 # Convert spec >=tot-1 to >=RXX.
239 tot_str = spec[spec.index('tot'):]
240 spec = spec.replace(
241 tot_str, self.tot_manager.convert_tot_spec(tot_str))
242
243 if spec.startswith('>='):
244 self._numeric_constraint = version.LooseVersion(
245 spec.lstrip('>=R'))
246 self._version_gte_constraint = True
247 elif spec.startswith('<='):
248 self._numeric_constraint = version.LooseVersion(
249 spec.lstrip('<=R'))
250 self._version_lte_constraint = True
251 elif spec.startswith('=='):
252 self._version_equal_constraint = True
253 self._numeric_constraint = version.LooseVersion(
254 spec.lstrip('==R'))
255 else:
256 self._bare_branches.append(spec)
257
258 def _fits_spec(self, branch):
259 """Check if a branch is deemed OK by this task's branch specs.
260
261 Will return whether a branch 'fits' the specifications stored in this task.
262
263 Examples:
264 Assuming tot=R40
265 t = Task('Name', 'suite', ['factory', '>=tot-1'])
266 t._fits_spec('factory') # True
267 t._fits_spec('40') # True
268 t._fits_spec('38') # False
269 t._fits_spec('firmware') # False
270
271 Args:
272 branch: the branch to check.
273
274 Returns:
275 True if branch 'fits' with stored specs, False otherwise.
276 """
277 if branch in build_lib.BARE_BRANCHES:
278 return branch in self._bare_branches
279
280 if self._numeric_constraint:
281 if self._version_equal_constraint:
282 return version.LooseVersion(branch) == self._numeric_constraint
283 elif self._version_gte_constraint:
284 return version.LooseVersion(branch) >= self._numeric_constraint
285 elif self._version_lte_constraint:
286 return version.LooseVersion(branch) <= self._numeric_constraint
287 else:
288 return version.LooseVersion(branch) >= self._numeric_constraint
289 else:
290 return False
291
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700292 def _get_latest_firmware_build_from_lab_config(self, spec, board,
293 lab_config):
294 """Get latest firmware from lab config file.
295
296 Args:
297 spec: a string build spec for RO or RW firmware, eg. firmware,
298 cros. For RO firmware, the value can also be released_ro_X.
299 board: a string board against which this task will run suite job.
300 lab_config: a config.LabConfig object, to read lab config file.
301
302 Returns:
303 A string latest firmware build.
304
305 Raises:
306 ValueError: if no firmware build list is found in lab config file.
307 """
308 index = int(spec[len(_RELEASE_RO_FIRMWARE_SPEC):])
309 released_ro_builds = lab_config.get_firmware_ro_build_list(
310 'RELEASED_RO_BUILDS_%s' % board).split(',')
311 if len(released_ro_builds) < index:
312 raise ValueError('No %dth ro_builds in the lab_config firmware '
313 'list %r' % (index, released_ro_builds))
314
315 logging.debug('Get ro_build: %s', released_ro_builds[index - 1])
316 return released_ro_builds[index - 1]
317
Xinan Lin028f9582019-12-11 10:55:33 -0800318 def _get_firmware_build(self, spec, board, firmware_build_dict, lab_config):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700319 """Get the firmware build name to test with ChromeOS build.
320
321 Args:
322 spec: a string build spec for RO or RW firmware, eg. firmware,
323 cros. For RO firmware, the value can also be released_ro_X.
324 board: a string board against which this task will run suite job.
Xinan Lin028f9582019-12-11 10:55:33 -0800325 firmware_build_dict: a dict of firmware artifacts, see return value of
326 |base_event.get_firmware_builds|.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700327 lab_config: a config.LabConfig object, to read lab config file.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700328
329 Returns:
330 A string firmware build name.
331
332 Raises:
333 ValueError: if failing to get firmware from lab config file;
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700334 """
335 if not spec or spec == 'stable':
336 # TODO(crbug.com/577316): Query stable RO firmware.
337 logging.debug('%s RO firmware build is not supported.', spec)
338 return None
339
340 try:
341 if spec.startswith(_RELEASE_RO_FIRMWARE_SPEC):
342 # For RO firmware whose value is released_ro_X, where X is the index of
343 # the list defined in lab config file:
344 # CROS/RELEASED_RO_BUILDS_[board].
345 # For example, for spec 'released_ro_2', and lab config file
346 # CROS/RELEASED_RO_BUILDS_veyron_jerry: build1,build2,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600347 # return firmware RO build should be 'build2'.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700348 return self._get_latest_firmware_build_from_lab_config(
349 spec, board, lab_config)
Xinan Lin028f9582019-12-11 10:55:33 -0800350 elif firmware_build_dict:
351 return firmware_build_dict.get((spec, board), None)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700352 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800353 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700354 except ValueError as e:
355 logging.warning('Failed to get firmware from lab config file'
356 'for spec %s, board %s: %s', spec, board, str(e))
357 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700358
C Shapiro7f24a002017-12-05 14:25:09 -0700359 def _push_suite(
360 self,
361 board=None,
362 model=None,
363 cros_build=None,
364 firmware_rw_build=None,
365 firmware_ro_build=None,
366 test_source_build=None,
367 launch_control_build=None,
Xixuan Wubb74a372018-08-21 17:37:08 -0700368 run_prod_code=False,
Xixuan Wu028f6732019-04-11 14:47:42 -0700369 is_skylab=False,
370 override_pool='',
371 override_qs_account=''):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700372 """Schedule suite job for the task by pushing suites to SuiteQueue.
373
374 Args:
375 board: the board against which this suite job run.
C Shapiro7f24a002017-12-05 14:25:09 -0700376 model: the model name for unibuild.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700377 cros_build: the CrOS build of this suite job.
378 firmware_rw_build: Firmware RW build to run this suite job with.
379 firmware_ro_build: Firmware RO build to run this suite job with.
380 test_source_build: Test source build, used for server-side
381 packaging of this suite job.
382 launch_control_build: the launch control build of this suite job.
383 run_prod_code: If True, the suite will run the test code that lives
384 in prod aka the test code currently on the lab servers. If
385 False, the control files and test code for this suite run will
386 be retrieved from the build artifacts. Default is False.
Xixuan Wubb74a372018-08-21 17:37:08 -0700387 is_skylab: If True, schedule this suite to skylab, otherwise schedule
388 it to AFE. Default is False.
Xixuan Wu028f6732019-04-11 14:47:42 -0700389 override_pool: A string to indicate pool of quota scheduler.
390 override_qs_account: A string of quota scheduler account.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700391 """
392 android_build = None
393 testbed_build = None
394
395 if self.testbed_dut_count:
396 launch_control_build = '%s#%d' % (launch_control_build,
397 self.testbed_dut_count)
398 test_source_build = launch_control_build
399 board = '%s-%d' % (board, self.testbed_dut_count)
400
401 if launch_control_build:
402 if not self.testbed_dut_count:
403 android_build = launch_control_build
404 else:
405 testbed_build = launch_control_build
406
407 suite_job_parameters = {
408 'suite': self.suite,
409 'board': board,
C Shapiro7f24a002017-12-05 14:25:09 -0700410 'model': model,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700411 build_lib.BuildVersionKey.CROS_VERSION: cros_build,
412 build_lib.BuildVersionKey.FW_RW_VERSION: firmware_rw_build,
413 build_lib.BuildVersionKey.FW_RO_VERSION: firmware_ro_build,
414 build_lib.BuildVersionKey.ANDROID_BUILD_VERSION: android_build,
415 build_lib.BuildVersionKey.TESTBED_BUILD_VERSION: testbed_build,
416 'num': self.num,
417 'pool': self.pool,
418 'priority': self.priority,
419 'timeout': self.timeout,
420 'timeout_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
421 'max_runtime_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
Xixuan Wu2ba72652017-09-15 15:49:42 -0700422 'no_wait_for_results': not self.job_retry,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700423 'test_source_build': test_source_build,
424 'job_retry': self.job_retry,
425 'no_delay': self.no_delay,
Xixuan Wu80531932017-10-12 17:26:51 -0700426 'force': self.force,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700427 'run_prod_code': run_prod_code,
Xixuan Wubb74a372018-08-21 17:37:08 -0700428 'is_skylab': is_skylab,
Xinan Lin6e097382019-08-27 18:43:35 -0700429 'is_frontdoor': self.frontdoor,
Xixuan Wu028f6732019-04-11 14:47:42 -0700430 'override_pool': override_pool,
431 'override_qs_account': override_qs_account,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700432 }
433
Xinan Lin9e4917d2019-11-04 10:58:47 -0800434 task_executor.push(task_executor.SUITES_QUEUE,
435 tag=self.suite,
436 **suite_job_parameters)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700437 logging.info('Pushing task %r into taskqueue', suite_job_parameters)
Xixuan Wu5451a662017-10-17 10:57:40 -0700438 self.is_pushed = True
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700439
Xinan Lin028f9582019-12-11 10:55:33 -0800440 def _schedule_cros_builds(self, build_dict, firmware_build_dict, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700441 """Schedule tasks with branch builds.
442
443 Args:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600444 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800445 value of |build_lib.get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800446 firmware_build_dict: a dict of firmware artifact, see return value of
447 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700448 configs: A config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700449 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700450 lab_config = configs.lab_config
C Shapiro7f24a002017-12-05 14:25:09 -0700451 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
C Shapiro09108252019-08-01 14:52:52 -0500452 model_agnostic_cros_builds = set()
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700453 for (board, passed_model, build_type,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600454 milestone), manifest in build_dict.iteritems():
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700455 cros_build = str(build_lib.CrOSBuild(board, build_type, milestone,
456 manifest))
457 logging.info('Running %s on %s', self.name, cros_build)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700458 if self.exclude_boards and board in self.exclude_boards:
459 logging.debug('Board %s is in excluded board list: %s',
460 board, self.exclude_boards)
461 continue
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700462
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700463 if self.boards and board not in self.boards:
464 logging.debug('Board %s is not in supported board list: %s',
465 board, self.boards)
466 continue
467
468 # Check the fitness of the build's branch for task
469 branch_build_spec = _pick_branch(build_type, milestone)
470 if not self._fits_spec(branch_build_spec):
471 logging.debug("branch_build spec %s doesn't fit this task's "
472 "requirement: %s", branch_build_spec, self.branch_specs)
473 continue
474
475 firmware_rw_build = None
476 firmware_ro_build = None
477 if self.firmware_rw_build_spec or self.firmware_ro_build_spec:
478 firmware_rw_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800479 self.firmware_rw_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700480 firmware_ro_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800481 self.firmware_ro_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700482
483 if not firmware_ro_build and self.firmware_ro_build_spec:
484 logging.debug('No firmware ro build to run, skip running')
485 continue
486
487 if self.test_source == build_lib.BuildType.FIRMWARE_RW:
488 test_source_build = firmware_rw_build
489 else:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700490 # Default test source build to CrOS build if it's not specified.
491 # Past versions chose based on run_prod_code, but we no longer respect
492 # that option and scheduler settings should always set it to False.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700493 test_source_build = cros_build
494
Xixuan Wub4b2f412019-05-03 11:22:31 -0700495 hwtest_board = build_lib.reshape_board(board)
Xinan Lin0550f492020-01-21 16:25:53 -0800496
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000497 models = models_by_board.get(board, [None])
C Shapiro7f24a002017-12-05 14:25:09 -0700498
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000499 for model in models:
500 if ((passed_model is not None and model == passed_model) or
501 passed_model is None):
Xixuan Wua41efa22019-05-17 14:28:04 -0700502 full_model_name = '%s_%s' % (board, model)
503 # Respect exclude first.
504 if self.exclude_models and full_model_name in self.exclude_models:
505 logging.debug("Skip model %s as it's in exclude model list %s",
506 model, self.exclude_models)
507 continue
508
509 if self.models and full_model_name not in self.models:
510 logging.debug("Skip model %s as it's not in support model list %s",
511 model, self.models)
512 continue
513
C Shapiro09108252019-08-01 14:52:52 -0500514 explicit_model = model
515
516 if self.any_model:
Xinan Lin3ba18a02019-08-13 15:44:55 -0700517 explicit_model = None
C Shapiro09108252019-08-01 14:52:52 -0500518 unique_build = str(cros_build)
519 if unique_build in model_agnostic_cros_builds:
520 # Skip since we've already run with no explicit model set.
521 continue
522 model_agnostic_cros_builds.add(unique_build)
523
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000524 self._push_suite(
Xixuan Wub4b2f412019-05-03 11:22:31 -0700525 board=hwtest_board,
C Shapiro09108252019-08-01 14:52:52 -0500526 model=explicit_model,
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000527 cros_build=cros_build,
528 firmware_rw_build=firmware_rw_build,
529 firmware_ro_build=firmware_ro_build,
Xinan Lin0550f492020-01-21 16:25:53 -0800530 test_source_build=test_source_build)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700531
532 def _schedule_launch_control_builds(self, launch_control_builds):
533 """Schedule tasks with launch control builds.
534
535 Args:
536 launch_control_builds: the build dict for Android boards.
537 """
538 for board, launch_control_build in launch_control_builds.iteritems():
539 logging.debug('Running %s on %s', self.name, board)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700540 if self.exclude_boards and board in self.exclude_boards:
541 logging.debug('Board %s is in excluded board list: %s',
542 board, self.exclude_boards)
543 continue
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700544 if self.boards and board not in self.boards:
545 logging.debug('Board %s is not in supported board list: %s',
546 board, self.boards)
547 continue
548
549 for android_build in launch_control_build:
550 if not any([branch in android_build
551 for branch in self.launch_control_branches]):
552 logging.debug('Branch %s is not required to run for task '
553 '%s', android_build, self.name)
554 continue
555
556 self._push_suite(board=board,
557 test_source_build=android_build,
558 launch_control_build=android_build)
559
560
561def _pick_branch(build_type, milestone):
562 """Select branch based on build type.
563
564 If the build_type is a bare branch, return build_type as the build spec.
565 If the build_type is a normal CrOS branch, return milestone as the build
566 spec.
567
568 Args:
569 build_type: a string builder name, like 'release'.
570 milestone: a string milestone, like '55'.
571
572 Returns:
573 A string milestone if build_type represents CrOS build, otherwise
574 return build_type.
575 """
576 return build_type if build_type in build_lib.BARE_BRANCHES else milestone
Xinan Linae7d6372019-09-12 14:42:10 -0700577
578
579def _split_unibuilds(build_dict, configs):
580 """Split the uni-builds to all models under a board.
581
582 Args:
583 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800584 value of |build_lib.get_cros_builds|.
Xinan Linae7d6372019-09-12 14:42:10 -0700585 configs: a config_reader.Configs object.
586
587 Returns:
588 A build dict.
589 """
590 models_by_board = configs.lab_config.get_cros_model_map()
591 if not models_by_board:
592 return build_dict
593 all_branch_build_dict = {}
594 for (board, model, config, milestone), platform in build_dict.iteritems():
595 uni_build_models = models_by_board.get(board)
596 if uni_build_models is not None and model is None:
597 for uni_build_model in uni_build_models:
598 model_key = (board, uni_build_model, config, milestone)
599 _add_build_dict(all_branch_build_dict, model_key, platform)
600 continue
601 build_key = (board, model, config, milestone)
602 _add_build_dict(all_branch_build_dict, build_key, platform)
603
604 return all_branch_build_dict
605
606
607def _add_build_dict(build_dict, key, value):
608 """A wrapper to add or update an item in build_dict."""
609 cur_manifest = build_dict.get(key)
610 if cur_manifest is None:
611 build_dict[key] = value
612 return
613 build_dict[key] = max(
614 [cur_manifest, value], key=version.LooseVersion)