blob: 1329aaa854a455078d42668207904587747f8599 [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
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070020
21class SchedulingError(Exception):
22 """Raised to indicate a failure in scheduling a task."""
23
24
25class Task(object):
26 """Represents an entry from the suite_scheduler config file.
27
28 Each entry from the suite_scheduler config file maps one-to-one to a
29 Task. Each instance has enough information to schedule itself.
30 """
31
Xinan Lin33937d62020-04-14 14:41:23 -070032 def __init__(self,
33 task_info,
34 board_family_config={},
35 tot=None,
36 is_sanity=False):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070037 """Initialize a task instance.
38
39 Args:
40 task_info: a config_reader.TaskInfo object, which includes:
41 name, name of this task, e.g. 'NightlyPower'
42 suite, the name of the suite to run, e.g. 'graphics_per-day'
43 branch_specs, a pre-vetted iterable of branch specifiers,
44 e.g. ['>=R18', 'factory']
45 pool, the pool of machines to schedule tasks. Default is None.
46 num, the number of devices to shard the test suite. It could
47 be an Integer or None. By default it's None.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070048 board_families, a common separated list of board family to run this
49 task on. Boards belong to one of the board family in this list
50 would be added to task_info.boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070051 boards, a comma separated list of boards to run this task on. Default
Po-Hsien Wangdd833072018-08-16 18:09:20 -070052 is None, which allows this task to run on all boards. If same board
53 is specified in 'boards' and 'exclude_boards', we exclude this
54 board.
Xinan Linc8647112020-02-04 16:45:56 -080055 dimensions, a comma separated lists of labels. Each label is in
56 the form of 'key:value'.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070057 exclude_board_families, a common separated list of board family not to
58 run task on. Boards belong to one of the board family in this list
59 would be added to task_info.exclude_boards.
Po-Hsien Wang6d589732018-05-15 17:19:34 -070060 exclude_boards, a comma separated list of boards not to run this task
61 on. Default is None, which allows this task to run on all boards.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070062 If same board is specified in 'boards' and 'exclude_boards', we
63 exclude this board.
Xixuan Wu89897182019-01-03 15:28:01 -080064 models, a comma separated list of models to run this task on. Default
65 is None, which allows this task to run on all models. If same model
66 is specified in 'models' and 'exclude_models', we exclude this
67 model.
68 exclude_models, a comma separated list of models not to run this task
69 on. Default is None, which allows this task to run on all models.
C Shapiro09108252019-08-01 14:52:52 -050070 any_model, set to True to not pass the model parameter and allow
71 a test suite to run any/all models available for testing.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070072 priority, the string name of a priority from constants.Priorities.
73 timeout, the max lifetime of the suite in hours.
74 cros_build_spec, spec used to determine the ChromeOS build to test
75 with a firmware build, e.g., tot, R41 etc.
76 firmware_rw_build_spec, spec used to determine the firmware RW build
77 test with a ChromeOS build.
78 firmware_ro_build_spec, spec used to determine the firmware RO build
79 test with a ChromeOS build.
80 test_source, the source of test code when firmware will be updated in
81 the test. The value can be 'firmware_rw', 'firmware_ro' or 'cros'.
82 job_retry, set to True to enable job-level retry. Default is False.
Xixuan Wu80531932017-10-12 17:26:51 -070083 no_delay, set to True to raise the priority of this task in task.
84 force, set to True to schedule this suite no matter whether there's
85 duplicate jobs before.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070086 queue, so the suite jobs can start running tests with no waiting.
87 hour, an integer specifying the hour that a nightly run should be
88 triggered, default is set to 21.
89 day, an integer specifying the day of a week that a weekly run should
90 be triggered, default is set to 5 (Saturday).
91 os_type, type of OS, e.g., cros, brillo, android. Default is cros.
92 The argument is required for android/brillo builds.
93 launch_control_branches, comma separated string of launch control
94 branches. The argument is required and only applicable for
95 android/brillo builds.
96 launch_control_targets, comma separated string of build targets for
97 launch control builds. The argument is required and only
98 applicable for android/brillo builds.
99 testbed_dut_count, number of duts to test when using a testbed.
Xinan Lin4757d6f2020-03-24 22:20:31 -0700100 qs_account, quota account for the unmanaged pool which has enabled
101 Quota Scheduler.
Xixuan Wu83118dd2018-08-27 12:11:35 -0700102 board_family_config: A board family dictionary mapping board_family name
103 to its corresponding boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700104 tot: The tot manager for checking ToT. If it's None, a new tot_manager
105 instance will be initialized.
Xinan Lin33937d62020-04-14 14:41:23 -0700106 is_sanity: A boolean; true if we are running in sanity env.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700107 """
Xixuan Wu5451a662017-10-17 10:57:40 -0700108 # Indicate whether there're suites get pushed into taskqueue for this task.
109 self.is_pushed = False
110
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700111 self.branch_specs = task_info.branch_specs
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700112 self.cros_build_spec = task_info.cros_build_spec
Xixuan Wu008ee832017-10-12 16:59:34 -0700113 self.day = task_info.day
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700114 self.firmware_ro_build_spec = task_info.firmware_ro_build_spec
115 self.firmware_rw_build_spec = task_info.firmware_rw_build_spec
116 self.force = task_info.force
117 self.frontdoor = task_info.frontdoor
118 self.hour = task_info.hour
119 self.job_retry = task_info.job_retry
120 self.name = task_info.name
121 self.no_delay = task_info.no_delay
122 self.num = task_info.num
Craig Bergstrom58263d32018-04-26 14:11:35 -0600123 self.only_hwtest_sanity_required = task_info.only_hwtest_sanity_required
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700124 self.os_type = task_info.os_type
125 self.pool = task_info.pool
126 self.priority = task_info.priority
Xinan Lin4757d6f2020-03-24 22:20:31 -0700127 self.qs_account = task_info.qs_account
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700128 self.suite = task_info.suite
129 self.test_source = task_info.test_source
130 self.testbed_dut_count = task_info.testbed_dut_count
131 self.timeout = task_info.timeout
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700132
133 if task_info.lc_branches:
134 self.launch_control_branches = [
135 t.strip() for t in task_info.lc_branches.split(',')]
136 else:
137 self.launch_control_branches = []
138
139 if task_info.lc_targets:
140 self.launch_control_targets = [
141 t.strip() for t in task_info.lc_targets.split(',')]
142 else:
143 self.launch_control_targets = []
144
145 if task_info.boards:
146 self.boards = [t.strip() for t in task_info.boards.split(',')]
147 else:
148 self.boards = []
149
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700150 if task_info.exclude_boards:
151 self.exclude_boards = [
152 t.strip() for t in task_info.exclude_boards.split(',')]
153 else:
154 self.exclude_boards = []
155
Xixuan Wu89897182019-01-03 15:28:01 -0800156 if task_info.models:
157 self.models = [t.strip() for t in task_info.models.split(',')]
158 else:
159 self.models = []
160
161 if task_info.exclude_models:
162 self.exclude_models = [
163 t.strip() for t in task_info.exclude_models.split(',')]
164 else:
165 self.exclude_models = []
166
C Shapiro09108252019-08-01 14:52:52 -0500167 self.any_model = task_info.any_model
168
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700169 if task_info.board_families:
170 # Finetune the allowed boards list with board_families & boards.
171 families = [family.strip()
172 for family in task_info.board_families.split(',')]
173 for family in families:
174 self.boards += board_family_config.get(family, [])
Xixuan Wu89897182019-01-03 15:28:01 -0800175
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700176 if task_info.exclude_board_families:
177 # Finetune the disallowed boards list with exclude_board_families
178 # & exclude_boards.
179 families = [family.strip()
180 for family in task_info.exclude_board_families.split(',')]
181 for family in families:
182 self.exclude_boards += board_family_config.get(family, [])
183
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700184 if tot is None:
185 self.tot_manager = tot_manager.TotMilestoneManager()
186 else:
187 self.tot_manager = tot
188
Xinan Linc8647112020-02-04 16:45:56 -0800189 self.dimensions = task_info.dimensions
190
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700191 self._set_spec_compare_info()
Xinan Lin33937d62020-04-14 14:41:23 -0700192 # Sanity test does not have to upload metrics.
193 if not is_sanity:
194 self.job_section = analytics.ScheduleJobSection(task_info)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700195
Xinan Lin028f9582019-12-11 10:55:33 -0800196 def schedule(self, launch_control_builds, cros_builds_tuple,
197 firmware_builds, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700198 """Schedule the task by its settings.
199
200 Args:
201 launch_control_builds: the build dict for Android boards, see
202 return value of |get_launch_control_builds|.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600203 cros_builds_tuple: the two-tuple of build dicts for ChromeOS boards,
204 see return value of |get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800205 firmware_builds: a dict of firmware artifact, see return value of
206 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700207 configs: a config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700208
209 Raises:
210 SchedulingError: if tasks that should be scheduled fail to schedule.
Xixuan Wu5451a662017-10-17 10:57:40 -0700211
212 Returns:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700213 A boolean indicator; true if there were any suites related to this
214 task which got pushed into the suites queue.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700215 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700216 assert configs.lab_config is not None
Xixuan Wu5451a662017-10-17 10:57:40 -0700217 self.is_pushed = False
218
Craig Bergstrom58263d32018-04-26 14:11:35 -0600219 branch_builds, relaxed_builds = cros_builds_tuple
220 builds_dict = branch_builds
221 if self.only_hwtest_sanity_required:
Xinan Linae7d6372019-09-12 14:42:10 -0700222 builds_dict = _split_unibuilds(relaxed_builds, configs)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600223
Xinan Lindf0698a2020-02-05 22:38:11 -0800224 # Record all target boards and models into job section.
225 lab_config = configs.lab_config
226 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
227 boards = self.boards if self.boards else lab_config.get_cros_board_list()
228 for b in boards:
229 if self.exclude_boards and b in self.exclude_boards:
230 continue
231 self.job_section.add_board(b)
232 models = self.models or models_by_board.get(b, [])
233 for m in models:
234 if m and '%s_%s' % (b, m) not in self.exclude_models:
235 self.job_section.add_model(m)
236
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700237 logging.info('######## Scheduling task %s ########', self.name)
238 if self.os_type == build_lib.OS_TYPE_CROS:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600239 if not builds_dict:
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700240 logging.info('No CrOS build to run, skip running.')
241 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800242 self._schedule_cros_builds(builds_dict, firmware_builds, configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700243 else:
244 if not launch_control_builds:
245 logging.info('No Android build to run, skip running.')
246 else:
247 self._schedule_launch_control_builds(launch_control_builds)
Xinan Lindf0698a2020-02-05 22:38:11 -0800248 upload_result = False
249 try:
250 upload_result = self.job_section.upload()
251 # For any exceptions from BQ, only log it and move on.
252 except Exception as e: #pylint: disable=broad-except
253 logging.exception(str(e))
254 if not upload_result:
255 logging.warning('Failed to insert row: %r', self.job_section)
Xixuan Wu5451a662017-10-17 10:57:40 -0700256 return self.is_pushed
257
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700258 def _set_spec_compare_info(self):
259 """Set branch spec compare info for task for further check."""
260 self._bare_branches = []
261 self._version_equal_constraint = False
262 self._version_gte_constraint = False
263 self._version_lte_constraint = False
264
265 if not self.branch_specs:
266 # Any milestone is OK.
267 self._numeric_constraint = version.LooseVersion('0')
268 else:
269 self._numeric_constraint = None
270 for spec in self.branch_specs:
271 if 'tot' in spec.lower():
272 # Convert spec >=tot-1 to >=RXX.
273 tot_str = spec[spec.index('tot'):]
274 spec = spec.replace(
275 tot_str, self.tot_manager.convert_tot_spec(tot_str))
276
277 if spec.startswith('>='):
278 self._numeric_constraint = version.LooseVersion(
279 spec.lstrip('>=R'))
280 self._version_gte_constraint = True
281 elif spec.startswith('<='):
282 self._numeric_constraint = version.LooseVersion(
283 spec.lstrip('<=R'))
284 self._version_lte_constraint = True
285 elif spec.startswith('=='):
286 self._version_equal_constraint = True
287 self._numeric_constraint = version.LooseVersion(
288 spec.lstrip('==R'))
289 else:
290 self._bare_branches.append(spec)
291
292 def _fits_spec(self, branch):
293 """Check if a branch is deemed OK by this task's branch specs.
294
295 Will return whether a branch 'fits' the specifications stored in this task.
296
297 Examples:
298 Assuming tot=R40
299 t = Task('Name', 'suite', ['factory', '>=tot-1'])
300 t._fits_spec('factory') # True
301 t._fits_spec('40') # True
302 t._fits_spec('38') # False
303 t._fits_spec('firmware') # False
304
305 Args:
306 branch: the branch to check.
307
308 Returns:
309 True if branch 'fits' with stored specs, False otherwise.
310 """
311 if branch in build_lib.BARE_BRANCHES:
312 return branch in self._bare_branches
313
314 if self._numeric_constraint:
315 if self._version_equal_constraint:
316 return version.LooseVersion(branch) == self._numeric_constraint
317 elif self._version_gte_constraint:
318 return version.LooseVersion(branch) >= self._numeric_constraint
319 elif self._version_lte_constraint:
320 return version.LooseVersion(branch) <= self._numeric_constraint
321 else:
322 return version.LooseVersion(branch) >= self._numeric_constraint
323 else:
324 return False
325
Xinan Lin028f9582019-12-11 10:55:33 -0800326 def _get_firmware_build(self, spec, board, firmware_build_dict, lab_config):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700327 """Get the firmware build name to test with ChromeOS build.
328
329 Args:
330 spec: a string build spec for RO or RW firmware, eg. firmware,
331 cros. For RO firmware, the value can also be released_ro_X.
332 board: a string board against which this task will run suite job.
Xinan Lin028f9582019-12-11 10:55:33 -0800333 firmware_build_dict: a dict of firmware artifacts, see return value of
334 |base_event.get_firmware_builds|.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700335 lab_config: a config.LabConfig object, to read lab config file.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700336
337 Returns:
338 A string firmware build name.
339
340 Raises:
341 ValueError: if failing to get firmware from lab config file;
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700342 """
343 if not spec or spec == 'stable':
344 # TODO(crbug.com/577316): Query stable RO firmware.
345 logging.debug('%s RO firmware build is not supported.', spec)
346 return None
347
348 try:
Dhanya Ganeshf6014b72020-08-04 22:33:28 +0000349 if firmware_build_dict:
Xinan Lin028f9582019-12-11 10:55:33 -0800350 return firmware_build_dict.get((spec, board), None)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700351 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800352 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700353 except ValueError as e:
354 logging.warning('Failed to get firmware from lab config file'
355 'for spec %s, board %s: %s', spec, board, str(e))
356 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700357
C Shapiro7f24a002017-12-05 14:25:09 -0700358 def _push_suite(
359 self,
Xinan Lindf0698a2020-02-05 22:38:11 -0800360 task_id=None,
C Shapiro7f24a002017-12-05 14:25:09 -0700361 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,
Xinan Lin4757d6f2020-03-24 22:20:31 -0700368 run_prod_code=False):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700369 """Schedule suite job for the task by pushing suites to SuiteQueue.
370
371 Args:
Xinan Lindf0698a2020-02-05 22:38:11 -0800372 task_id: the id to track this task in exectuion.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700373 board: the board against which this suite job run.
C Shapiro7f24a002017-12-05 14:25:09 -0700374 model: the model name for unibuild.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700375 cros_build: the CrOS build of this suite job.
376 firmware_rw_build: Firmware RW build to run this suite job with.
377 firmware_ro_build: Firmware RO build to run this suite job with.
378 test_source_build: Test source build, used for server-side
379 packaging of this suite job.
380 launch_control_build: the launch control build of this suite job.
381 run_prod_code: If True, the suite will run the test code that lives
382 in prod aka the test code currently on the lab servers. If
383 False, the control files and test code for this suite run will
384 be retrieved from the build artifacts. Default is False.
385 """
386 android_build = None
387 testbed_build = None
388
389 if self.testbed_dut_count:
390 launch_control_build = '%s#%d' % (launch_control_build,
391 self.testbed_dut_count)
392 test_source_build = launch_control_build
393 board = '%s-%d' % (board, self.testbed_dut_count)
394
395 if launch_control_build:
396 if not self.testbed_dut_count:
397 android_build = launch_control_build
398 else:
399 testbed_build = launch_control_build
400
401 suite_job_parameters = {
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700402 build_lib.BuildVersionKey.ANDROID_BUILD_VERSION: android_build,
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700403 build_lib.BuildVersionKey.CROS_VERSION: cros_build,
404 build_lib.BuildVersionKey.FW_RO_VERSION: firmware_ro_build,
405 build_lib.BuildVersionKey.FW_RW_VERSION: firmware_rw_build,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700406 build_lib.BuildVersionKey.TESTBED_BUILD_VERSION: testbed_build,
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700407 'board': board,
408 'dimensions': self.dimensions,
409 'force': self.force,
410 'job_retry': self.job_retry,
411 'max_runtime_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
412 'model': model,
413 'name': self.name,
414 'no_delay': self.no_delay,
415 'no_wait_for_results': not self.job_retry,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700416 'num': self.num,
417 'pool': self.pool,
418 'priority': self.priority,
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700419 'qs_account': self.qs_account,
420 'run_prod_code': run_prod_code,
421 'suite': self.suite,
422 'task_id': task_id,
423 'test_source_build': test_source_build,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700424 'timeout': self.timeout,
425 'timeout_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700426 }
427
Xinan Lin9e4917d2019-11-04 10:58:47 -0800428 task_executor.push(task_executor.SUITES_QUEUE,
429 tag=self.suite,
430 **suite_job_parameters)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700431 logging.info('Pushing task %r into taskqueue', suite_job_parameters)
Xixuan Wu5451a662017-10-17 10:57:40 -0700432 self.is_pushed = True
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700433
Xinan Lin028f9582019-12-11 10:55:33 -0800434 def _schedule_cros_builds(self, build_dict, firmware_build_dict, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700435 """Schedule tasks with branch builds.
436
437 Args:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600438 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800439 value of |build_lib.get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800440 firmware_build_dict: a dict of firmware artifact, see return value of
441 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700442 configs: A config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700443 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700444 lab_config = configs.lab_config
C Shapiro7f24a002017-12-05 14:25:09 -0700445 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
C Shapiro09108252019-08-01 14:52:52 -0500446 model_agnostic_cros_builds = set()
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700447 for (board, passed_model, build_type,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600448 milestone), manifest in build_dict.iteritems():
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700449 cros_build = str(build_lib.CrOSBuild(board, build_type, milestone,
450 manifest))
451 logging.info('Running %s on %s', self.name, cros_build)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700452 if self.exclude_boards and board in self.exclude_boards:
453 logging.debug('Board %s is in excluded board list: %s',
454 board, self.exclude_boards)
455 continue
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700456
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700457 if self.boards and board not in self.boards:
458 logging.debug('Board %s is not in supported board list: %s',
459 board, self.boards)
460 continue
461
462 # Check the fitness of the build's branch for task
463 branch_build_spec = _pick_branch(build_type, milestone)
464 if not self._fits_spec(branch_build_spec):
Xinan Lindf0698a2020-02-05 22:38:11 -0800465 msg = ("branch_build spec %s doesn't fit this task's "
466 "requirement: %s") % (branch_build_spec,
467 ",".join(self.branch_specs))
468 logging.debug(msg)
469 self.job_section.add_schedule_job(board, passed_model, msg=msg)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700470 continue
471
Xinan Lindf0698a2020-02-05 22:38:11 -0800472 # Record this build as it matches both board and branch specs.
473 if self.only_hwtest_sanity_required:
474 self.job_section.add_matched_relax_build(
475 board, build_type, milestone, manifest)
476 else:
477 self.job_section.add_matched_build(
478 board, build_type, milestone, manifest)
479
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700480 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:
Xinan Lindf0698a2020-02-05 22:38:11 -0800489 msg = 'No RO firmware ro build to run, skip running'
490 logging.debug(msg)
491 self.job_section.add_schedule_job(board, passed_model, msg=msg)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700492 continue
493
494 if self.test_source == build_lib.BuildType.FIRMWARE_RW:
495 test_source_build = firmware_rw_build
496 else:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700497 # Default test source build to CrOS build if it's not specified.
498 # Past versions chose based on run_prod_code, but we no longer respect
499 # that option and scheduler settings should always set it to False.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700500 test_source_build = cros_build
501
Xinan Lindf0698a2020-02-05 22:38:11 -0800502 # Record the matched firwmare build.
503 if firmware_rw_build:
504 self.job_section.add_matched_fw_build(
505 board,
506 self.firmware_rw_build_spec,
507 firmware_rw_build,
508 read_only=False)
509 if firmware_ro_build:
510 self.job_section.add_matched_fw_build(
511 board,
512 self.firmware_ro_build_spec,
513 firmware_ro_build,
514 read_only=True)
515
Xinan Lin6668e0f2020-05-29 10:02:57 -0700516 # Board above is used as build target to control the CrOS image.
517 # The following part is to assign models for lab boards, where
518 # the suffix should be removed.
Prathmesh Prabhu074b8d42020-08-13 17:17:33 +0000519 hwtest_board = build_lib.reshape_board(board)
520
Xinan Lin6668e0f2020-05-29 10:02:57 -0700521 models = models_by_board.get(hwtest_board, [None])
C Shapiro7f24a002017-12-05 14:25:09 -0700522
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000523 for model in models:
524 if ((passed_model is not None and model == passed_model) or
525 passed_model is None):
Xinan Lin6668e0f2020-05-29 10:02:57 -0700526 full_model_name = '%s_%s' % (hwtest_board, model)
Xixuan Wua41efa22019-05-17 14:28:04 -0700527 # Respect exclude first.
528 if self.exclude_models and full_model_name in self.exclude_models:
529 logging.debug("Skip model %s as it's in exclude model list %s",
530 model, self.exclude_models)
531 continue
532
533 if self.models and full_model_name not in self.models:
534 logging.debug("Skip model %s as it's not in support model list %s",
535 model, self.models)
536 continue
537
C Shapiro09108252019-08-01 14:52:52 -0500538 explicit_model = model
539
540 if self.any_model:
Xinan Lin3ba18a02019-08-13 15:44:55 -0700541 explicit_model = None
C Shapiro09108252019-08-01 14:52:52 -0500542 unique_build = str(cros_build)
543 if unique_build in model_agnostic_cros_builds:
544 # Skip since we've already run with no explicit model set.
Xinan Lindf0698a2020-02-05 22:38:11 -0800545 msg = "Skip model %s as any_model enabled for this job." % model
546 self.job_section.add_schedule_job(board, model, msg=msg)
C Shapiro09108252019-08-01 14:52:52 -0500547 continue
548 model_agnostic_cros_builds.add(unique_build)
549
Xinan Lindf0698a2020-02-05 22:38:11 -0800550 task_id = str(uuid.uuid1())
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000551 self._push_suite(
Xinan Lindf0698a2020-02-05 22:38:11 -0800552 task_id=task_id,
Xixuan Wub4b2f412019-05-03 11:22:31 -0700553 board=hwtest_board,
C Shapiro09108252019-08-01 14:52:52 -0500554 model=explicit_model,
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000555 cros_build=cros_build,
556 firmware_rw_build=firmware_rw_build,
557 firmware_ro_build=firmware_ro_build,
Xinan Lin0550f492020-01-21 16:25:53 -0800558 test_source_build=test_source_build)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700559
Xinan Lin6668e0f2020-05-29 10:02:57 -0700560 # Analytics table stores the build target instead of the lab board.
Xinan Lin0673a182020-04-14 15:09:35 -0700561 self.job_section.add_schedule_job(
562 board, explicit_model, task_id=task_id)
Xinan Lindf0698a2020-02-05 22:38:11 -0800563
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700564 def _schedule_launch_control_builds(self, launch_control_builds):
565 """Schedule tasks with launch control builds.
566
567 Args:
568 launch_control_builds: the build dict for Android boards.
569 """
570 for board, launch_control_build in launch_control_builds.iteritems():
571 logging.debug('Running %s on %s', self.name, board)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700572 if self.exclude_boards and board in self.exclude_boards:
573 logging.debug('Board %s is in excluded board list: %s',
574 board, self.exclude_boards)
575 continue
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700576 if self.boards and board not in self.boards:
577 logging.debug('Board %s is not in supported board list: %s',
578 board, self.boards)
579 continue
580
581 for android_build in launch_control_build:
582 if not any([branch in android_build
583 for branch in self.launch_control_branches]):
584 logging.debug('Branch %s is not required to run for task '
585 '%s', android_build, self.name)
586 continue
587
588 self._push_suite(board=board,
589 test_source_build=android_build,
590 launch_control_build=android_build)
591
592
593def _pick_branch(build_type, milestone):
594 """Select branch based on build type.
595
596 If the build_type is a bare branch, return build_type as the build spec.
597 If the build_type is a normal CrOS branch, return milestone as the build
598 spec.
599
600 Args:
601 build_type: a string builder name, like 'release'.
602 milestone: a string milestone, like '55'.
603
604 Returns:
605 A string milestone if build_type represents CrOS build, otherwise
606 return build_type.
607 """
608 return build_type if build_type in build_lib.BARE_BRANCHES else milestone
Xinan Linae7d6372019-09-12 14:42:10 -0700609
610
611def _split_unibuilds(build_dict, configs):
612 """Split the uni-builds to all models under a board.
613
614 Args:
615 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800616 value of |build_lib.get_cros_builds|.
Xinan Linae7d6372019-09-12 14:42:10 -0700617 configs: a config_reader.Configs object.
618
619 Returns:
620 A build dict.
621 """
622 models_by_board = configs.lab_config.get_cros_model_map()
623 if not models_by_board:
624 return build_dict
625 all_branch_build_dict = {}
626 for (board, model, config, milestone), platform in build_dict.iteritems():
627 uni_build_models = models_by_board.get(board)
628 if uni_build_models is not None and model is None:
629 for uni_build_model in uni_build_models:
630 model_key = (board, uni_build_model, config, milestone)
631 _add_build_dict(all_branch_build_dict, model_key, platform)
632 continue
633 build_key = (board, model, config, milestone)
634 _add_build_dict(all_branch_build_dict, build_key, platform)
635
636 return all_branch_build_dict
637
638
639def _add_build_dict(build_dict, key, value):
640 """A wrapper to add or update an item in build_dict."""
641 cur_manifest = build_dict.get(key)
642 if cur_manifest is None:
643 build_dict[key] = value
644 return
645 build_dict[key] = max(
646 [cur_manifest, value], key=version.LooseVersion)