blob: 9c6b6d9f065f393855616d5a75edec2f714f7b04 [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.
Xinan Linc8647112020-02-04 16:45:56 -080052 dimensions, a comma separated lists of labels. Each label is in
53 the form of 'key:value'.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070054 exclude_board_families, a common separated list of board family not to
55 run task on. Boards belong to one of the board family in this list
56 would be added to task_info.exclude_boards.
Po-Hsien Wang6d589732018-05-15 17:19:34 -070057 exclude_boards, a comma separated list of boards not to run this task
58 on. Default is None, which allows this task to run on all boards.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070059 If same board is specified in 'boards' and 'exclude_boards', we
60 exclude this board.
Xixuan Wu89897182019-01-03 15:28:01 -080061 models, a comma separated list of models to run this task on. Default
62 is None, which allows this task to run on all models. If same model
63 is specified in 'models' and 'exclude_models', we exclude this
64 model.
65 exclude_models, a comma separated list of models not to run this task
66 on. Default is None, which allows this task to run on all models.
C Shapiro09108252019-08-01 14:52:52 -050067 any_model, set to True to not pass the model parameter and allow
68 a test suite to run any/all models available for testing.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070069 priority, the string name of a priority from constants.Priorities.
70 timeout, the max lifetime of the suite in hours.
71 cros_build_spec, spec used to determine the ChromeOS build to test
72 with a firmware build, e.g., tot, R41 etc.
73 firmware_rw_build_spec, spec used to determine the firmware RW build
74 test with a ChromeOS build.
75 firmware_ro_build_spec, spec used to determine the firmware RO build
76 test with a ChromeOS build.
77 test_source, the source of test code when firmware will be updated in
78 the test. The value can be 'firmware_rw', 'firmware_ro' or 'cros'.
79 job_retry, set to True to enable job-level retry. Default is False.
Xixuan Wu80531932017-10-12 17:26:51 -070080 no_delay, set to True to raise the priority of this task in task.
81 force, set to True to schedule this suite no matter whether there's
82 duplicate jobs before.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070083 queue, so the suite jobs can start running tests with no waiting.
84 hour, an integer specifying the hour that a nightly run should be
85 triggered, default is set to 21.
86 day, an integer specifying the day of a week that a weekly run should
87 be triggered, default is set to 5 (Saturday).
88 os_type, type of OS, e.g., cros, brillo, android. Default is cros.
89 The argument is required for android/brillo builds.
90 launch_control_branches, comma separated string of launch control
91 branches. The argument is required and only applicable for
92 android/brillo builds.
93 launch_control_targets, comma separated string of build targets for
94 launch control builds. The argument is required and only
95 applicable for android/brillo builds.
96 testbed_dut_count, number of duts to test when using a testbed.
Xixuan Wu83118dd2018-08-27 12:11:35 -070097 board_family_config: A board family dictionary mapping board_family name
98 to its corresponding boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070099 tot: The tot manager for checking ToT. If it's None, a new tot_manager
100 instance will be initialized.
101 """
Xixuan Wu5451a662017-10-17 10:57:40 -0700102 # Indicate whether there're suites get pushed into taskqueue for this task.
103 self.is_pushed = False
104
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700105 self.name = task_info.name
106 self.suite = task_info.suite
107 self.branch_specs = task_info.branch_specs
108 self.pool = task_info.pool
109 self.num = task_info.num
110 self.priority = task_info.priority
111 self.timeout = task_info.timeout
112 self.cros_build_spec = task_info.cros_build_spec
113 self.firmware_rw_build_spec = task_info.firmware_rw_build_spec
114 self.firmware_ro_build_spec = task_info.firmware_ro_build_spec
115 self.test_source = task_info.test_source
116 self.job_retry = task_info.job_retry
117 self.no_delay = task_info.no_delay
Xixuan Wu80531932017-10-12 17:26:51 -0700118 self.force = task_info.force
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700119 self.os_type = task_info.os_type
Xinan Lin3ba18a02019-08-13 15:44:55 -0700120 self.frontdoor = task_info.frontdoor
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700121 self.testbed_dut_count = task_info.testbed_dut_count
Xixuan Wu008ee832017-10-12 16:59:34 -0700122 self.hour = task_info.hour
123 self.day = task_info.day
Craig Bergstrom58263d32018-04-26 14:11:35 -0600124 self.only_hwtest_sanity_required = task_info.only_hwtest_sanity_required
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700125
126 if task_info.lc_branches:
127 self.launch_control_branches = [
128 t.strip() for t in task_info.lc_branches.split(',')]
129 else:
130 self.launch_control_branches = []
131
132 if task_info.lc_targets:
133 self.launch_control_targets = [
134 t.strip() for t in task_info.lc_targets.split(',')]
135 else:
136 self.launch_control_targets = []
137
138 if task_info.boards:
139 self.boards = [t.strip() for t in task_info.boards.split(',')]
140 else:
141 self.boards = []
142
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700143 if task_info.exclude_boards:
144 self.exclude_boards = [
145 t.strip() for t in task_info.exclude_boards.split(',')]
146 else:
147 self.exclude_boards = []
148
Xixuan Wu89897182019-01-03 15:28:01 -0800149 if task_info.models:
150 self.models = [t.strip() for t in task_info.models.split(',')]
151 else:
152 self.models = []
153
154 if task_info.exclude_models:
155 self.exclude_models = [
156 t.strip() for t in task_info.exclude_models.split(',')]
157 else:
158 self.exclude_models = []
159
C Shapiro09108252019-08-01 14:52:52 -0500160 self.any_model = task_info.any_model
161
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700162 if task_info.board_families:
163 # Finetune the allowed boards list with board_families & boards.
164 families = [family.strip()
165 for family in task_info.board_families.split(',')]
166 for family in families:
167 self.boards += board_family_config.get(family, [])
Xixuan Wu89897182019-01-03 15:28:01 -0800168
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700169 if task_info.exclude_board_families:
170 # Finetune the disallowed boards list with exclude_board_families
171 # & exclude_boards.
172 families = [family.strip()
173 for family in task_info.exclude_board_families.split(',')]
174 for family in families:
175 self.exclude_boards += board_family_config.get(family, [])
176
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700177 if tot is None:
178 self.tot_manager = tot_manager.TotMilestoneManager()
179 else:
180 self.tot_manager = tot
181
Xinan Linc8647112020-02-04 16:45:56 -0800182 self.dimensions = task_info.dimensions
183
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700184 self._set_spec_compare_info()
185
Xinan Lin028f9582019-12-11 10:55:33 -0800186 def schedule(self, launch_control_builds, cros_builds_tuple,
187 firmware_builds, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700188 """Schedule the task by its settings.
189
190 Args:
191 launch_control_builds: the build dict for Android boards, see
192 return value of |get_launch_control_builds|.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600193 cros_builds_tuple: the two-tuple of build dicts for ChromeOS boards,
194 see return value of |get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800195 firmware_builds: a dict of firmware artifact, see return value of
196 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700197 configs: a config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700198
199 Raises:
200 SchedulingError: if tasks that should be scheduled fail to schedule.
Xixuan Wu5451a662017-10-17 10:57:40 -0700201
202 Returns:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700203 A boolean indicator; true if there were any suites related to this
204 task which got pushed into the suites queue.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700205 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700206 assert configs.lab_config is not None
Xixuan Wu5451a662017-10-17 10:57:40 -0700207 self.is_pushed = False
208
Craig Bergstrom58263d32018-04-26 14:11:35 -0600209 branch_builds, relaxed_builds = cros_builds_tuple
210 builds_dict = branch_builds
211 if self.only_hwtest_sanity_required:
Xinan Linae7d6372019-09-12 14:42:10 -0700212 builds_dict = _split_unibuilds(relaxed_builds, configs)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600213
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700214 logging.info('######## Scheduling task %s ########', self.name)
215 if self.os_type == build_lib.OS_TYPE_CROS:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600216 if not builds_dict:
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700217 logging.info('No CrOS build to run, skip running.')
218 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800219 self._schedule_cros_builds(builds_dict, firmware_builds, configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700220 else:
221 if not launch_control_builds:
222 logging.info('No Android build to run, skip running.')
223 else:
224 self._schedule_launch_control_builds(launch_control_builds)
225
Xixuan Wu5451a662017-10-17 10:57:40 -0700226 return self.is_pushed
227
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700228 def _set_spec_compare_info(self):
229 """Set branch spec compare info for task for further check."""
230 self._bare_branches = []
231 self._version_equal_constraint = False
232 self._version_gte_constraint = False
233 self._version_lte_constraint = False
234
235 if not self.branch_specs:
236 # Any milestone is OK.
237 self._numeric_constraint = version.LooseVersion('0')
238 else:
239 self._numeric_constraint = None
240 for spec in self.branch_specs:
241 if 'tot' in spec.lower():
242 # Convert spec >=tot-1 to >=RXX.
243 tot_str = spec[spec.index('tot'):]
244 spec = spec.replace(
245 tot_str, self.tot_manager.convert_tot_spec(tot_str))
246
247 if spec.startswith('>='):
248 self._numeric_constraint = version.LooseVersion(
249 spec.lstrip('>=R'))
250 self._version_gte_constraint = True
251 elif spec.startswith('<='):
252 self._numeric_constraint = version.LooseVersion(
253 spec.lstrip('<=R'))
254 self._version_lte_constraint = True
255 elif spec.startswith('=='):
256 self._version_equal_constraint = True
257 self._numeric_constraint = version.LooseVersion(
258 spec.lstrip('==R'))
259 else:
260 self._bare_branches.append(spec)
261
262 def _fits_spec(self, branch):
263 """Check if a branch is deemed OK by this task's branch specs.
264
265 Will return whether a branch 'fits' the specifications stored in this task.
266
267 Examples:
268 Assuming tot=R40
269 t = Task('Name', 'suite', ['factory', '>=tot-1'])
270 t._fits_spec('factory') # True
271 t._fits_spec('40') # True
272 t._fits_spec('38') # False
273 t._fits_spec('firmware') # False
274
275 Args:
276 branch: the branch to check.
277
278 Returns:
279 True if branch 'fits' with stored specs, False otherwise.
280 """
281 if branch in build_lib.BARE_BRANCHES:
282 return branch in self._bare_branches
283
284 if self._numeric_constraint:
285 if self._version_equal_constraint:
286 return version.LooseVersion(branch) == self._numeric_constraint
287 elif self._version_gte_constraint:
288 return version.LooseVersion(branch) >= self._numeric_constraint
289 elif self._version_lte_constraint:
290 return version.LooseVersion(branch) <= self._numeric_constraint
291 else:
292 return version.LooseVersion(branch) >= self._numeric_constraint
293 else:
294 return False
295
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700296 def _get_latest_firmware_build_from_lab_config(self, spec, board,
297 lab_config):
298 """Get latest firmware from lab config file.
299
300 Args:
301 spec: a string build spec for RO or RW firmware, eg. firmware,
302 cros. For RO firmware, the value can also be released_ro_X.
303 board: a string board against which this task will run suite job.
304 lab_config: a config.LabConfig object, to read lab config file.
305
306 Returns:
307 A string latest firmware build.
308
309 Raises:
310 ValueError: if no firmware build list is found in lab config file.
311 """
312 index = int(spec[len(_RELEASE_RO_FIRMWARE_SPEC):])
313 released_ro_builds = lab_config.get_firmware_ro_build_list(
314 'RELEASED_RO_BUILDS_%s' % board).split(',')
315 if len(released_ro_builds) < index:
316 raise ValueError('No %dth ro_builds in the lab_config firmware '
317 'list %r' % (index, released_ro_builds))
318
319 logging.debug('Get ro_build: %s', released_ro_builds[index - 1])
320 return released_ro_builds[index - 1]
321
Xinan Lin028f9582019-12-11 10:55:33 -0800322 def _get_firmware_build(self, spec, board, firmware_build_dict, lab_config):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700323 """Get the firmware build name to test with ChromeOS build.
324
325 Args:
326 spec: a string build spec for RO or RW firmware, eg. firmware,
327 cros. For RO firmware, the value can also be released_ro_X.
328 board: a string board against which this task will run suite job.
Xinan Lin028f9582019-12-11 10:55:33 -0800329 firmware_build_dict: a dict of firmware artifacts, see return value of
330 |base_event.get_firmware_builds|.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700331 lab_config: a config.LabConfig object, to read lab config file.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700332
333 Returns:
334 A string firmware build name.
335
336 Raises:
337 ValueError: if failing to get firmware from lab config file;
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700338 """
339 if not spec or spec == 'stable':
340 # TODO(crbug.com/577316): Query stable RO firmware.
341 logging.debug('%s RO firmware build is not supported.', spec)
342 return None
343
344 try:
345 if spec.startswith(_RELEASE_RO_FIRMWARE_SPEC):
346 # For RO firmware whose value is released_ro_X, where X is the index of
347 # the list defined in lab config file:
348 # CROS/RELEASED_RO_BUILDS_[board].
349 # For example, for spec 'released_ro_2', and lab config file
350 # CROS/RELEASED_RO_BUILDS_veyron_jerry: build1,build2,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600351 # return firmware RO build should be 'build2'.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700352 return self._get_latest_firmware_build_from_lab_config(
353 spec, board, lab_config)
Xinan Lin028f9582019-12-11 10:55:33 -0800354 elif firmware_build_dict:
355 return firmware_build_dict.get((spec, board), None)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700356 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800357 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700358 except ValueError as e:
359 logging.warning('Failed to get firmware from lab config file'
360 'for spec %s, board %s: %s', spec, board, str(e))
361 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700362
C Shapiro7f24a002017-12-05 14:25:09 -0700363 def _push_suite(
364 self,
365 board=None,
366 model=None,
367 cros_build=None,
368 firmware_rw_build=None,
369 firmware_ro_build=None,
370 test_source_build=None,
371 launch_control_build=None,
Xixuan Wubb74a372018-08-21 17:37:08 -0700372 run_prod_code=False,
Xixuan Wu028f6732019-04-11 14:47:42 -0700373 is_skylab=False,
374 override_pool='',
375 override_qs_account=''):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700376 """Schedule suite job for the task by pushing suites to SuiteQueue.
377
378 Args:
379 board: the board against which this suite job run.
C Shapiro7f24a002017-12-05 14:25:09 -0700380 model: the model name for unibuild.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700381 cros_build: the CrOS build of this suite job.
382 firmware_rw_build: Firmware RW build to run this suite job with.
383 firmware_ro_build: Firmware RO build to run this suite job with.
384 test_source_build: Test source build, used for server-side
385 packaging of this suite job.
386 launch_control_build: the launch control build of this suite job.
387 run_prod_code: If True, the suite will run the test code that lives
388 in prod aka the test code currently on the lab servers. If
389 False, the control files and test code for this suite run will
390 be retrieved from the build artifacts. Default is False.
Xixuan Wubb74a372018-08-21 17:37:08 -0700391 is_skylab: If True, schedule this suite to skylab, otherwise schedule
392 it to AFE. Default is False.
Xixuan Wu028f6732019-04-11 14:47:42 -0700393 override_pool: A string to indicate pool of quota scheduler.
394 override_qs_account: A string of quota scheduler account.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700395 """
396 android_build = None
397 testbed_build = None
398
399 if self.testbed_dut_count:
400 launch_control_build = '%s#%d' % (launch_control_build,
401 self.testbed_dut_count)
402 test_source_build = launch_control_build
403 board = '%s-%d' % (board, self.testbed_dut_count)
404
405 if launch_control_build:
406 if not self.testbed_dut_count:
407 android_build = launch_control_build
408 else:
409 testbed_build = launch_control_build
410
411 suite_job_parameters = {
412 'suite': self.suite,
413 'board': board,
C Shapiro7f24a002017-12-05 14:25:09 -0700414 'model': model,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700415 build_lib.BuildVersionKey.CROS_VERSION: cros_build,
416 build_lib.BuildVersionKey.FW_RW_VERSION: firmware_rw_build,
417 build_lib.BuildVersionKey.FW_RO_VERSION: firmware_ro_build,
418 build_lib.BuildVersionKey.ANDROID_BUILD_VERSION: android_build,
419 build_lib.BuildVersionKey.TESTBED_BUILD_VERSION: testbed_build,
420 'num': self.num,
421 'pool': self.pool,
422 'priority': self.priority,
423 'timeout': self.timeout,
424 'timeout_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
425 'max_runtime_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
Xixuan Wu2ba72652017-09-15 15:49:42 -0700426 'no_wait_for_results': not self.job_retry,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700427 'test_source_build': test_source_build,
428 'job_retry': self.job_retry,
429 'no_delay': self.no_delay,
Xixuan Wu80531932017-10-12 17:26:51 -0700430 'force': self.force,
Xinan Linc8647112020-02-04 16:45:56 -0800431 'dimensions': self.dimensions,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700432 'run_prod_code': run_prod_code,
Xixuan Wubb74a372018-08-21 17:37:08 -0700433 'is_skylab': is_skylab,
Xinan Lin6e097382019-08-27 18:43:35 -0700434 'is_frontdoor': self.frontdoor,
Xixuan Wu028f6732019-04-11 14:47:42 -0700435 'override_pool': override_pool,
436 'override_qs_account': override_qs_account,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700437 }
438
Xinan Lin9e4917d2019-11-04 10:58:47 -0800439 task_executor.push(task_executor.SUITES_QUEUE,
440 tag=self.suite,
441 **suite_job_parameters)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700442 logging.info('Pushing task %r into taskqueue', suite_job_parameters)
Xixuan Wu5451a662017-10-17 10:57:40 -0700443 self.is_pushed = True
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700444
Xinan Lin028f9582019-12-11 10:55:33 -0800445 def _schedule_cros_builds(self, build_dict, firmware_build_dict, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700446 """Schedule tasks with branch builds.
447
448 Args:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600449 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800450 value of |build_lib.get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800451 firmware_build_dict: a dict of firmware artifact, see return value of
452 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700453 configs: A config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700454 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700455 lab_config = configs.lab_config
C Shapiro7f24a002017-12-05 14:25:09 -0700456 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
C Shapiro09108252019-08-01 14:52:52 -0500457 model_agnostic_cros_builds = set()
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700458 for (board, passed_model, build_type,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600459 milestone), manifest in build_dict.iteritems():
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700460 cros_build = str(build_lib.CrOSBuild(board, build_type, milestone,
461 manifest))
462 logging.info('Running %s on %s', self.name, cros_build)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700463 if self.exclude_boards and board in self.exclude_boards:
464 logging.debug('Board %s is in excluded board list: %s',
465 board, self.exclude_boards)
466 continue
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700467
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700468 if self.boards and board not in self.boards:
469 logging.debug('Board %s is not in supported board list: %s',
470 board, self.boards)
471 continue
472
473 # Check the fitness of the build's branch for task
474 branch_build_spec = _pick_branch(build_type, milestone)
475 if not self._fits_spec(branch_build_spec):
476 logging.debug("branch_build spec %s doesn't fit this task's "
477 "requirement: %s", branch_build_spec, self.branch_specs)
478 continue
479
480 firmware_rw_build = None
481 firmware_ro_build = None
482 if self.firmware_rw_build_spec or self.firmware_ro_build_spec:
483 firmware_rw_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800484 self.firmware_rw_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700485 firmware_ro_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800486 self.firmware_ro_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700487
488 if not firmware_ro_build and self.firmware_ro_build_spec:
489 logging.debug('No firmware ro build to run, skip running')
490 continue
491
492 if self.test_source == build_lib.BuildType.FIRMWARE_RW:
493 test_source_build = firmware_rw_build
494 else:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700495 # Default test source build to CrOS build if it's not specified.
496 # Past versions chose based on run_prod_code, but we no longer respect
497 # that option and scheduler settings should always set it to False.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700498 test_source_build = cros_build
499
Xixuan Wub4b2f412019-05-03 11:22:31 -0700500 hwtest_board = build_lib.reshape_board(board)
Xinan Lin0550f492020-01-21 16:25:53 -0800501
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000502 models = models_by_board.get(board, [None])
C Shapiro7f24a002017-12-05 14:25:09 -0700503
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000504 for model in models:
505 if ((passed_model is not None and model == passed_model) or
506 passed_model is None):
Xixuan Wua41efa22019-05-17 14:28:04 -0700507 full_model_name = '%s_%s' % (board, model)
508 # Respect exclude first.
509 if self.exclude_models and full_model_name in self.exclude_models:
510 logging.debug("Skip model %s as it's in exclude model list %s",
511 model, self.exclude_models)
512 continue
513
514 if self.models and full_model_name not in self.models:
515 logging.debug("Skip model %s as it's not in support model list %s",
516 model, self.models)
517 continue
518
C Shapiro09108252019-08-01 14:52:52 -0500519 explicit_model = model
520
521 if self.any_model:
Xinan Lin3ba18a02019-08-13 15:44:55 -0700522 explicit_model = None
C Shapiro09108252019-08-01 14:52:52 -0500523 unique_build = str(cros_build)
524 if unique_build in model_agnostic_cros_builds:
525 # Skip since we've already run with no explicit model set.
526 continue
527 model_agnostic_cros_builds.add(unique_build)
528
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000529 self._push_suite(
Xixuan Wub4b2f412019-05-03 11:22:31 -0700530 board=hwtest_board,
C Shapiro09108252019-08-01 14:52:52 -0500531 model=explicit_model,
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000532 cros_build=cros_build,
533 firmware_rw_build=firmware_rw_build,
534 firmware_ro_build=firmware_ro_build,
Xinan Lin0550f492020-01-21 16:25:53 -0800535 test_source_build=test_source_build)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700536
537 def _schedule_launch_control_builds(self, launch_control_builds):
538 """Schedule tasks with launch control builds.
539
540 Args:
541 launch_control_builds: the build dict for Android boards.
542 """
543 for board, launch_control_build in launch_control_builds.iteritems():
544 logging.debug('Running %s on %s', self.name, board)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700545 if self.exclude_boards and board in self.exclude_boards:
546 logging.debug('Board %s is in excluded board list: %s',
547 board, self.exclude_boards)
548 continue
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700549 if self.boards and board not in self.boards:
550 logging.debug('Board %s is not in supported board list: %s',
551 board, self.boards)
552 continue
553
554 for android_build in launch_control_build:
555 if not any([branch in android_build
556 for branch in self.launch_control_branches]):
557 logging.debug('Branch %s is not required to run for task '
558 '%s', android_build, self.name)
559 continue
560
561 self._push_suite(board=board,
562 test_source_build=android_build,
563 launch_control_build=android_build)
564
565
566def _pick_branch(build_type, milestone):
567 """Select branch based on build type.
568
569 If the build_type is a bare branch, return build_type as the build spec.
570 If the build_type is a normal CrOS branch, return milestone as the build
571 spec.
572
573 Args:
574 build_type: a string builder name, like 'release'.
575 milestone: a string milestone, like '55'.
576
577 Returns:
578 A string milestone if build_type represents CrOS build, otherwise
579 return build_type.
580 """
581 return build_type if build_type in build_lib.BARE_BRANCHES else milestone
Xinan Linae7d6372019-09-12 14:42:10 -0700582
583
584def _split_unibuilds(build_dict, configs):
585 """Split the uni-builds to all models under a board.
586
587 Args:
588 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800589 value of |build_lib.get_cros_builds|.
Xinan Linae7d6372019-09-12 14:42:10 -0700590 configs: a config_reader.Configs object.
591
592 Returns:
593 A build dict.
594 """
595 models_by_board = configs.lab_config.get_cros_model_map()
596 if not models_by_board:
597 return build_dict
598 all_branch_build_dict = {}
599 for (board, model, config, milestone), platform in build_dict.iteritems():
600 uni_build_models = models_by_board.get(board)
601 if uni_build_models is not None and model is None:
602 for uni_build_model in uni_build_models:
603 model_key = (board, uni_build_model, config, milestone)
604 _add_build_dict(all_branch_build_dict, model_key, platform)
605 continue
606 build_key = (board, model, config, milestone)
607 _add_build_dict(all_branch_build_dict, build_key, platform)
608
609 return all_branch_build_dict
610
611
612def _add_build_dict(build_dict, key, value):
613 """A wrapper to add or update an item in build_dict."""
614 cur_manifest = build_dict.get(key)
615 if cur_manifest is None:
616 build_dict[key] = value
617 return
618 build_dict[key] = max(
619 [cur_manifest, value], key=version.LooseVersion)