blob: 64f592128c9df194dcb0801a60e46d02df0983d3 [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
Xixuan Wued878ea2019-03-18 15:32:16 -070012import skylab
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070013import task_executor
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070014import tot_manager
15
16# The max lifetime of a suite scheduled by suite scheduler
17_JOB_MAX_RUNTIME_MINS_DEFAULT = 72 * 60
18
19# One kind of formats for RO firmware spec.
20_RELEASE_RO_FIRMWARE_SPEC = 'released_ro_'
21
22
23class SchedulingError(Exception):
24 """Raised to indicate a failure in scheduling a task."""
25
26
27class Task(object):
28 """Represents an entry from the suite_scheduler config file.
29
30 Each entry from the suite_scheduler config file maps one-to-one to a
31 Task. Each instance has enough information to schedule itself.
32 """
33
Po-Hsien Wangdd833072018-08-16 18:09:20 -070034 def __init__(self, task_info, board_family_config={}, tot=None):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070035 """Initialize a task instance.
36
37 Args:
38 task_info: a config_reader.TaskInfo object, which includes:
39 name, name of this task, e.g. 'NightlyPower'
40 suite, the name of the suite to run, e.g. 'graphics_per-day'
41 branch_specs, a pre-vetted iterable of branch specifiers,
42 e.g. ['>=R18', 'factory']
43 pool, the pool of machines to schedule tasks. Default is None.
44 num, the number of devices to shard the test suite. It could
45 be an Integer or None. By default it's None.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070046 board_families, a common separated list of board family to run this
47 task on. Boards belong to one of the board family in this list
48 would be added to task_info.boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070049 boards, a comma separated list of boards to run this task on. Default
Po-Hsien Wangdd833072018-08-16 18:09:20 -070050 is None, which allows this task to run on all boards. If same board
51 is specified in 'boards' and 'exclude_boards', we exclude this
52 board.
53 exclude_board_families, a common separated list of board family not to
54 run task on. Boards belong to one of the board family in this list
55 would be added to task_info.exclude_boards.
Po-Hsien Wang6d589732018-05-15 17:19:34 -070056 exclude_boards, a comma separated list of boards not to run this task
57 on. Default is None, which allows this task to run on all boards.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070058 If same board is specified in 'boards' and 'exclude_boards', we
59 exclude this board.
Xixuan Wu89897182019-01-03 15:28:01 -080060 models, a comma separated list of models to run this task on. Default
61 is None, which allows this task to run on all models. If same model
62 is specified in 'models' and 'exclude_models', we exclude this
63 model.
64 exclude_models, a comma separated list of models not to run this task
65 on. Default is None, which allows this task to run on all models.
C Shapiro09108252019-08-01 14:52:52 -050066 any_model, set to True to not pass the model parameter and allow
67 a test suite to run any/all models available for testing.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070068 priority, the string name of a priority from constants.Priorities.
69 timeout, the max lifetime of the suite in hours.
70 cros_build_spec, spec used to determine the ChromeOS build to test
71 with a firmware build, e.g., tot, R41 etc.
72 firmware_rw_build_spec, spec used to determine the firmware RW build
73 test with a ChromeOS build.
74 firmware_ro_build_spec, spec used to determine the firmware RO build
75 test with a ChromeOS build.
76 test_source, the source of test code when firmware will be updated in
77 the test. The value can be 'firmware_rw', 'firmware_ro' or 'cros'.
78 job_retry, set to True to enable job-level retry. Default is False.
Xixuan Wu80531932017-10-12 17:26:51 -070079 no_delay, set to True to raise the priority of this task in task.
80 force, set to True to schedule this suite no matter whether there's
81 duplicate jobs before.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070082 queue, so the suite jobs can start running tests with no waiting.
83 hour, an integer specifying the hour that a nightly run should be
84 triggered, default is set to 21.
85 day, an integer specifying the day of a week that a weekly run should
86 be triggered, default is set to 5 (Saturday).
87 os_type, type of OS, e.g., cros, brillo, android. Default is cros.
88 The argument is required for android/brillo builds.
89 launch_control_branches, comma separated string of launch control
90 branches. The argument is required and only applicable for
91 android/brillo builds.
92 launch_control_targets, comma separated string of build targets for
93 launch control builds. The argument is required and only
94 applicable for android/brillo builds.
95 testbed_dut_count, number of duts to test when using a testbed.
Xixuan Wu83118dd2018-08-27 12:11:35 -070096 board_family_config: A board family dictionary mapping board_family name
97 to its corresponding boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070098 tot: The tot manager for checking ToT. If it's None, a new tot_manager
99 instance will be initialized.
100 """
Xixuan Wu5451a662017-10-17 10:57:40 -0700101 # Indicate whether there're suites get pushed into taskqueue for this task.
102 self.is_pushed = False
103
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700104 self.name = task_info.name
105 self.suite = task_info.suite
106 self.branch_specs = task_info.branch_specs
107 self.pool = task_info.pool
108 self.num = task_info.num
109 self.priority = task_info.priority
110 self.timeout = task_info.timeout
111 self.cros_build_spec = task_info.cros_build_spec
112 self.firmware_rw_build_spec = task_info.firmware_rw_build_spec
113 self.firmware_ro_build_spec = task_info.firmware_ro_build_spec
114 self.test_source = task_info.test_source
115 self.job_retry = task_info.job_retry
116 self.no_delay = task_info.no_delay
Xixuan Wu80531932017-10-12 17:26:51 -0700117 self.force = task_info.force
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700118 self.os_type = task_info.os_type
Xinan Lin3ba18a02019-08-13 15:44:55 -0700119 self.frontdoor = task_info.frontdoor
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700120 self.testbed_dut_count = task_info.testbed_dut_count
Xixuan Wu008ee832017-10-12 16:59:34 -0700121 self.hour = task_info.hour
122 self.day = task_info.day
Craig Bergstrom58263d32018-04-26 14:11:35 -0600123 self.only_hwtest_sanity_required = task_info.only_hwtest_sanity_required
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700124
125 if task_info.lc_branches:
126 self.launch_control_branches = [
127 t.strip() for t in task_info.lc_branches.split(',')]
128 else:
129 self.launch_control_branches = []
130
131 if task_info.lc_targets:
132 self.launch_control_targets = [
133 t.strip() for t in task_info.lc_targets.split(',')]
134 else:
135 self.launch_control_targets = []
136
137 if task_info.boards:
138 self.boards = [t.strip() for t in task_info.boards.split(',')]
139 else:
140 self.boards = []
141
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700142 if task_info.exclude_boards:
143 self.exclude_boards = [
144 t.strip() for t in task_info.exclude_boards.split(',')]
145 else:
146 self.exclude_boards = []
147
Xixuan Wu89897182019-01-03 15:28:01 -0800148 if task_info.models:
149 self.models = [t.strip() for t in task_info.models.split(',')]
150 else:
151 self.models = []
152
153 if task_info.exclude_models:
154 self.exclude_models = [
155 t.strip() for t in task_info.exclude_models.split(',')]
156 else:
157 self.exclude_models = []
158
C Shapiro09108252019-08-01 14:52:52 -0500159 self.any_model = task_info.any_model
160
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700161 if task_info.board_families:
162 # Finetune the allowed boards list with board_families & boards.
163 families = [family.strip()
164 for family in task_info.board_families.split(',')]
165 for family in families:
166 self.boards += board_family_config.get(family, [])
Xixuan Wu89897182019-01-03 15:28:01 -0800167
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700168 if task_info.exclude_board_families:
169 # Finetune the disallowed boards list with exclude_board_families
170 # & exclude_boards.
171 families = [family.strip()
172 for family in task_info.exclude_board_families.split(',')]
173 for family in families:
174 self.exclude_boards += board_family_config.get(family, [])
175
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700176 if tot is None:
177 self.tot_manager = tot_manager.TotMilestoneManager()
178 else:
179 self.tot_manager = tot
180
181 self._set_spec_compare_info()
182
Xinan Lin028f9582019-12-11 10:55:33 -0800183 def schedule(self, launch_control_builds, cros_builds_tuple,
184 firmware_builds, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700185 """Schedule the task by its settings.
186
187 Args:
188 launch_control_builds: the build dict for Android boards, see
189 return value of |get_launch_control_builds|.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600190 cros_builds_tuple: the two-tuple of build dicts for ChromeOS boards,
191 see return value of |get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800192 firmware_builds: a dict of firmware artifact, see return value of
193 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700194 configs: a config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700195
196 Raises:
197 SchedulingError: if tasks that should be scheduled fail to schedule.
Xixuan Wu5451a662017-10-17 10:57:40 -0700198
199 Returns:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700200 A boolean indicator; true if there were any suites related to this
201 task which got pushed into the suites queue.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700202 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700203 assert configs.lab_config is not None
Xixuan Wu5451a662017-10-17 10:57:40 -0700204 self.is_pushed = False
205
Craig Bergstrom58263d32018-04-26 14:11:35 -0600206 branch_builds, relaxed_builds = cros_builds_tuple
207 builds_dict = branch_builds
208 if self.only_hwtest_sanity_required:
Xinan Linae7d6372019-09-12 14:42:10 -0700209 builds_dict = _split_unibuilds(relaxed_builds, configs)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600210
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700211 logging.info('######## Scheduling task %s ########', self.name)
212 if self.os_type == build_lib.OS_TYPE_CROS:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600213 if not builds_dict:
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700214 logging.info('No CrOS build to run, skip running.')
215 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800216 self._schedule_cros_builds(builds_dict, firmware_builds, configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700217 else:
218 if not launch_control_builds:
219 logging.info('No Android build to run, skip running.')
220 else:
221 self._schedule_launch_control_builds(launch_control_builds)
222
Xixuan Wu5451a662017-10-17 10:57:40 -0700223 return self.is_pushed
224
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700225 def _set_spec_compare_info(self):
226 """Set branch spec compare info for task for further check."""
227 self._bare_branches = []
228 self._version_equal_constraint = False
229 self._version_gte_constraint = False
230 self._version_lte_constraint = False
231
232 if not self.branch_specs:
233 # Any milestone is OK.
234 self._numeric_constraint = version.LooseVersion('0')
235 else:
236 self._numeric_constraint = None
237 for spec in self.branch_specs:
238 if 'tot' in spec.lower():
239 # Convert spec >=tot-1 to >=RXX.
240 tot_str = spec[spec.index('tot'):]
241 spec = spec.replace(
242 tot_str, self.tot_manager.convert_tot_spec(tot_str))
243
244 if spec.startswith('>='):
245 self._numeric_constraint = version.LooseVersion(
246 spec.lstrip('>=R'))
247 self._version_gte_constraint = True
248 elif spec.startswith('<='):
249 self._numeric_constraint = version.LooseVersion(
250 spec.lstrip('<=R'))
251 self._version_lte_constraint = True
252 elif spec.startswith('=='):
253 self._version_equal_constraint = True
254 self._numeric_constraint = version.LooseVersion(
255 spec.lstrip('==R'))
256 else:
257 self._bare_branches.append(spec)
258
259 def _fits_spec(self, branch):
260 """Check if a branch is deemed OK by this task's branch specs.
261
262 Will return whether a branch 'fits' the specifications stored in this task.
263
264 Examples:
265 Assuming tot=R40
266 t = Task('Name', 'suite', ['factory', '>=tot-1'])
267 t._fits_spec('factory') # True
268 t._fits_spec('40') # True
269 t._fits_spec('38') # False
270 t._fits_spec('firmware') # False
271
272 Args:
273 branch: the branch to check.
274
275 Returns:
276 True if branch 'fits' with stored specs, False otherwise.
277 """
278 if branch in build_lib.BARE_BRANCHES:
279 return branch in self._bare_branches
280
281 if self._numeric_constraint:
282 if self._version_equal_constraint:
283 return version.LooseVersion(branch) == self._numeric_constraint
284 elif self._version_gte_constraint:
285 return version.LooseVersion(branch) >= self._numeric_constraint
286 elif self._version_lte_constraint:
287 return version.LooseVersion(branch) <= self._numeric_constraint
288 else:
289 return version.LooseVersion(branch) >= self._numeric_constraint
290 else:
291 return False
292
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700293 def _get_latest_firmware_build_from_lab_config(self, spec, board,
294 lab_config):
295 """Get latest firmware from lab config file.
296
297 Args:
298 spec: a string build spec for RO or RW firmware, eg. firmware,
299 cros. For RO firmware, the value can also be released_ro_X.
300 board: a string board against which this task will run suite job.
301 lab_config: a config.LabConfig object, to read lab config file.
302
303 Returns:
304 A string latest firmware build.
305
306 Raises:
307 ValueError: if no firmware build list is found in lab config file.
308 """
309 index = int(spec[len(_RELEASE_RO_FIRMWARE_SPEC):])
310 released_ro_builds = lab_config.get_firmware_ro_build_list(
311 'RELEASED_RO_BUILDS_%s' % board).split(',')
312 if len(released_ro_builds) < index:
313 raise ValueError('No %dth ro_builds in the lab_config firmware '
314 'list %r' % (index, released_ro_builds))
315
316 logging.debug('Get ro_build: %s', released_ro_builds[index - 1])
317 return released_ro_builds[index - 1]
318
Xinan Lin028f9582019-12-11 10:55:33 -0800319 def _get_firmware_build(self, spec, board, firmware_build_dict, lab_config):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700320 """Get the firmware build name to test with ChromeOS build.
321
322 Args:
323 spec: a string build spec for RO or RW firmware, eg. firmware,
324 cros. For RO firmware, the value can also be released_ro_X.
325 board: a string board against which this task will run suite job.
Xinan Lin028f9582019-12-11 10:55:33 -0800326 firmware_build_dict: a dict of firmware artifacts, see return value of
327 |base_event.get_firmware_builds|.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700328 lab_config: a config.LabConfig object, to read lab config file.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700329
330 Returns:
331 A string firmware build name.
332
333 Raises:
334 ValueError: if failing to get firmware from lab config file;
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700335 """
336 if not spec or spec == 'stable':
337 # TODO(crbug.com/577316): Query stable RO firmware.
338 logging.debug('%s RO firmware build is not supported.', spec)
339 return None
340
341 try:
342 if spec.startswith(_RELEASE_RO_FIRMWARE_SPEC):
343 # For RO firmware whose value is released_ro_X, where X is the index of
344 # the list defined in lab config file:
345 # CROS/RELEASED_RO_BUILDS_[board].
346 # For example, for spec 'released_ro_2', and lab config file
347 # CROS/RELEASED_RO_BUILDS_veyron_jerry: build1,build2,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600348 # return firmware RO build should be 'build2'.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700349 return self._get_latest_firmware_build_from_lab_config(
350 spec, board, lab_config)
Xinan Lin028f9582019-12-11 10:55:33 -0800351 elif firmware_build_dict:
352 return firmware_build_dict.get((spec, board), None)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700353 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800354 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700355 except ValueError as e:
356 logging.warning('Failed to get firmware from lab config file'
357 'for spec %s, board %s: %s', spec, board, str(e))
358 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700359
C Shapiro7f24a002017-12-05 14:25:09 -0700360 def _push_suite(
361 self,
362 board=None,
363 model=None,
364 cros_build=None,
365 firmware_rw_build=None,
366 firmware_ro_build=None,
367 test_source_build=None,
368 launch_control_build=None,
Xixuan Wubb74a372018-08-21 17:37:08 -0700369 run_prod_code=False,
Xixuan Wu028f6732019-04-11 14:47:42 -0700370 is_skylab=False,
371 override_pool='',
372 override_qs_account=''):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700373 """Schedule suite job for the task by pushing suites to SuiteQueue.
374
375 Args:
376 board: the board against which this suite job run.
C Shapiro7f24a002017-12-05 14:25:09 -0700377 model: the model name for unibuild.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700378 cros_build: the CrOS build of this suite job.
379 firmware_rw_build: Firmware RW build to run this suite job with.
380 firmware_ro_build: Firmware RO build to run this suite job with.
381 test_source_build: Test source build, used for server-side
382 packaging of this suite job.
383 launch_control_build: the launch control build of this suite job.
384 run_prod_code: If True, the suite will run the test code that lives
385 in prod aka the test code currently on the lab servers. If
386 False, the control files and test code for this suite run will
387 be retrieved from the build artifacts. Default is False.
Xixuan Wubb74a372018-08-21 17:37:08 -0700388 is_skylab: If True, schedule this suite to skylab, otherwise schedule
389 it to AFE. Default is False.
Xixuan Wu028f6732019-04-11 14:47:42 -0700390 override_pool: A string to indicate pool of quota scheduler.
391 override_qs_account: A string of quota scheduler account.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700392 """
393 android_build = None
394 testbed_build = None
395
396 if self.testbed_dut_count:
397 launch_control_build = '%s#%d' % (launch_control_build,
398 self.testbed_dut_count)
399 test_source_build = launch_control_build
400 board = '%s-%d' % (board, self.testbed_dut_count)
401
402 if launch_control_build:
403 if not self.testbed_dut_count:
404 android_build = launch_control_build
405 else:
406 testbed_build = launch_control_build
407
408 suite_job_parameters = {
409 'suite': self.suite,
410 'board': board,
C Shapiro7f24a002017-12-05 14:25:09 -0700411 'model': model,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700412 build_lib.BuildVersionKey.CROS_VERSION: cros_build,
413 build_lib.BuildVersionKey.FW_RW_VERSION: firmware_rw_build,
414 build_lib.BuildVersionKey.FW_RO_VERSION: firmware_ro_build,
415 build_lib.BuildVersionKey.ANDROID_BUILD_VERSION: android_build,
416 build_lib.BuildVersionKey.TESTBED_BUILD_VERSION: testbed_build,
417 'num': self.num,
418 'pool': self.pool,
419 'priority': self.priority,
420 'timeout': self.timeout,
421 'timeout_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
422 'max_runtime_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
Xixuan Wu2ba72652017-09-15 15:49:42 -0700423 'no_wait_for_results': not self.job_retry,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700424 'test_source_build': test_source_build,
425 'job_retry': self.job_retry,
426 'no_delay': self.no_delay,
Xixuan Wu80531932017-10-12 17:26:51 -0700427 'force': self.force,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700428 'run_prod_code': run_prod_code,
Xixuan Wubb74a372018-08-21 17:37:08 -0700429 'is_skylab': is_skylab,
Xinan Lin6e097382019-08-27 18:43:35 -0700430 'is_frontdoor': self.frontdoor,
Xixuan Wu028f6732019-04-11 14:47:42 -0700431 'override_pool': override_pool,
432 'override_qs_account': override_qs_account,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700433 }
434
Xinan Lin9e4917d2019-11-04 10:58:47 -0800435 task_executor.push(task_executor.SUITES_QUEUE,
436 tag=self.suite,
437 **suite_job_parameters)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700438 logging.info('Pushing task %r into taskqueue', suite_job_parameters)
Xixuan Wu5451a662017-10-17 10:57:40 -0700439 self.is_pushed = True
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700440
Xinan Lin028f9582019-12-11 10:55:33 -0800441 def _schedule_cros_builds(self, build_dict, firmware_build_dict, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700442 """Schedule tasks with branch builds.
443
444 Args:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600445 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800446 value of |build_lib.get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800447 firmware_build_dict: a dict of firmware artifact, see return value of
448 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700449 configs: A config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700450 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700451 lab_config = configs.lab_config
C Shapiro7f24a002017-12-05 14:25:09 -0700452 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
C Shapiro09108252019-08-01 14:52:52 -0500453 model_agnostic_cros_builds = set()
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700454 for (board, passed_model, build_type,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600455 milestone), manifest in build_dict.iteritems():
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700456 cros_build = str(build_lib.CrOSBuild(board, build_type, milestone,
457 manifest))
458 logging.info('Running %s on %s', self.name, cros_build)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700459 if self.exclude_boards and board in self.exclude_boards:
460 logging.debug('Board %s is in excluded board list: %s',
461 board, self.exclude_boards)
462 continue
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700463
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700464 if self.boards and board not in self.boards:
465 logging.debug('Board %s is not in supported board list: %s',
466 board, self.boards)
467 continue
468
469 # Check the fitness of the build's branch for task
470 branch_build_spec = _pick_branch(build_type, milestone)
471 if not self._fits_spec(branch_build_spec):
472 logging.debug("branch_build spec %s doesn't fit this task's "
473 "requirement: %s", branch_build_spec, self.branch_specs)
474 continue
475
476 firmware_rw_build = None
477 firmware_ro_build = None
478 if self.firmware_rw_build_spec or self.firmware_ro_build_spec:
479 firmware_rw_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800480 self.firmware_rw_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700481 firmware_ro_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800482 self.firmware_ro_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700483
484 if not firmware_ro_build and self.firmware_ro_build_spec:
485 logging.debug('No firmware ro build to run, skip running')
486 continue
487
488 if self.test_source == build_lib.BuildType.FIRMWARE_RW:
489 test_source_build = firmware_rw_build
490 else:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700491 # Default test source build to CrOS build if it's not specified.
492 # Past versions chose based on run_prod_code, but we no longer respect
493 # that option and scheduler settings should always set it to False.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700494 test_source_build = cros_build
495
Xixuan Wub4b2f412019-05-03 11:22:31 -0700496 hwtest_board = build_lib.reshape_board(board)
Xixuan Wu1f0be4c2019-03-18 16:53:23 -0700497 is_skylab = skylab.should_run_in_skylab(configs.migration_config,
Xixuan Wub4b2f412019-05-03 11:22:31 -0700498 hwtest_board,
Xixuan Wued878ea2019-03-18 15:32:16 -0700499 passed_model,
Xixuan Wu1e42c752019-03-21 13:41:49 -0700500 self.suite,
501 self.pool)
Xixuan Wu028f6732019-04-11 14:47:42 -0700502 override_pool, override_qs_account = skylab.get_override_info(
503 configs.migration_config,
Xixuan Wub4b2f412019-05-03 11:22:31 -0700504 hwtest_board,
Xixuan Wu028f6732019-04-11 14:47:42 -0700505 passed_model,
506 self.suite,
507 self.pool)
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000508 models = models_by_board.get(board, [None])
C Shapiro7f24a002017-12-05 14:25:09 -0700509
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000510 for model in models:
511 if ((passed_model is not None and model == passed_model) or
512 passed_model is None):
Xixuan Wua41efa22019-05-17 14:28:04 -0700513 full_model_name = '%s_%s' % (board, model)
514 # Respect exclude first.
515 if self.exclude_models and full_model_name in self.exclude_models:
516 logging.debug("Skip model %s as it's in exclude model list %s",
517 model, self.exclude_models)
518 continue
519
520 if self.models and full_model_name not in self.models:
521 logging.debug("Skip model %s as it's not in support model list %s",
522 model, self.models)
523 continue
524
C Shapiro09108252019-08-01 14:52:52 -0500525 explicit_model = model
526
527 if self.any_model:
Xinan Lin3ba18a02019-08-13 15:44:55 -0700528 explicit_model = None
C Shapiro09108252019-08-01 14:52:52 -0500529 unique_build = str(cros_build)
530 if unique_build in model_agnostic_cros_builds:
531 # Skip since we've already run with no explicit model set.
532 continue
533 model_agnostic_cros_builds.add(unique_build)
534
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000535 self._push_suite(
Xixuan Wub4b2f412019-05-03 11:22:31 -0700536 board=hwtest_board,
C Shapiro09108252019-08-01 14:52:52 -0500537 model=explicit_model,
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000538 cros_build=cros_build,
539 firmware_rw_build=firmware_rw_build,
540 firmware_ro_build=firmware_ro_build,
541 test_source_build=test_source_build,
Xixuan Wu028f6732019-04-11 14:47:42 -0700542 is_skylab=is_skylab,
543 override_pool=override_pool,
544 override_qs_account=override_qs_account)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700545
546 def _schedule_launch_control_builds(self, launch_control_builds):
547 """Schedule tasks with launch control builds.
548
549 Args:
550 launch_control_builds: the build dict for Android boards.
551 """
552 for board, launch_control_build in launch_control_builds.iteritems():
553 logging.debug('Running %s on %s', self.name, board)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700554 if self.exclude_boards and board in self.exclude_boards:
555 logging.debug('Board %s is in excluded board list: %s',
556 board, self.exclude_boards)
557 continue
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700558 if self.boards and board not in self.boards:
559 logging.debug('Board %s is not in supported board list: %s',
560 board, self.boards)
561 continue
562
563 for android_build in launch_control_build:
564 if not any([branch in android_build
565 for branch in self.launch_control_branches]):
566 logging.debug('Branch %s is not required to run for task '
567 '%s', android_build, self.name)
568 continue
569
570 self._push_suite(board=board,
571 test_source_build=android_build,
572 launch_control_build=android_build)
573
574
575def _pick_branch(build_type, milestone):
576 """Select branch based on build type.
577
578 If the build_type is a bare branch, return build_type as the build spec.
579 If the build_type is a normal CrOS branch, return milestone as the build
580 spec.
581
582 Args:
583 build_type: a string builder name, like 'release'.
584 milestone: a string milestone, like '55'.
585
586 Returns:
587 A string milestone if build_type represents CrOS build, otherwise
588 return build_type.
589 """
590 return build_type if build_type in build_lib.BARE_BRANCHES else milestone
Xinan Linae7d6372019-09-12 14:42:10 -0700591
592
593def _split_unibuilds(build_dict, configs):
594 """Split the uni-builds to all models under a board.
595
596 Args:
597 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800598 value of |build_lib.get_cros_builds|.
Xinan Linae7d6372019-09-12 14:42:10 -0700599 configs: a config_reader.Configs object.
600
601 Returns:
602 A build dict.
603 """
604 models_by_board = configs.lab_config.get_cros_model_map()
605 if not models_by_board:
606 return build_dict
607 all_branch_build_dict = {}
608 for (board, model, config, milestone), platform in build_dict.iteritems():
609 uni_build_models = models_by_board.get(board)
610 if uni_build_models is not None and model is None:
611 for uni_build_model in uni_build_models:
612 model_key = (board, uni_build_model, config, milestone)
613 _add_build_dict(all_branch_build_dict, model_key, platform)
614 continue
615 build_key = (board, model, config, milestone)
616 _add_build_dict(all_branch_build_dict, build_key, platform)
617
618 return all_branch_build_dict
619
620
621def _add_build_dict(build_dict, key, value):
622 """A wrapper to add or update an item in build_dict."""
623 cur_manifest = build_dict.get(key)
624 if cur_manifest is None:
625 build_dict[key] = value
626 return
627 build_dict[key] = max(
628 [cur_manifest, value], key=version.LooseVersion)