blob: 3e1e6786931783e09e7cb7e6f78afde36bd029fc [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
Sean Abrahamec0d0762020-09-18 17:19:05 +000018_JOB_MAX_RUNTIME_MINS_DEFAULT = 72 * 60
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070019
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.
Taylor Clark8b06a832021-02-16 21:45:42 +000048 analytics_name, the build rule name
Po-Hsien Wangdd833072018-08-16 18:09:20 -070049 board_families, a common separated list of board family to run this
50 task on. Boards belong to one of the board family in this list
51 would be added to task_info.boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070052 boards, a comma separated list of boards to run this task on. Default
Po-Hsien Wangdd833072018-08-16 18:09:20 -070053 is None, which allows this task to run on all boards. If same board
54 is specified in 'boards' and 'exclude_boards', we exclude this
55 board.
Xinan Linc8647112020-02-04 16:45:56 -080056 dimensions, a comma separated lists of labels. Each label is in
57 the form of 'key:value'.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070058 exclude_board_families, a common separated list of board family not to
59 run task on. Boards belong to one of the board family in this list
60 would be added to task_info.exclude_boards.
Po-Hsien Wang6d589732018-05-15 17:19:34 -070061 exclude_boards, a comma separated list of boards not to run this task
62 on. Default is None, which allows this task to run on all boards.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070063 If same board is specified in 'boards' and 'exclude_boards', we
64 exclude this board.
Xixuan Wu89897182019-01-03 15:28:01 -080065 models, a comma separated list of models to run this task on. Default
66 is None, which allows this task to run on all models. If same model
67 is specified in 'models' and 'exclude_models', we exclude this
68 model.
69 exclude_models, a comma separated list of models not to run this task
70 on. Default is None, which allows this task to run on all models.
C Shapiro09108252019-08-01 14:52:52 -050071 any_model, set to True to not pass the model parameter and allow
72 a test suite to run any/all models available for testing.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070073 priority, the string name of a priority from constants.Priorities.
74 timeout, the max lifetime of the suite in hours.
75 cros_build_spec, spec used to determine the ChromeOS build to test
76 with a firmware build, e.g., tot, R41 etc.
77 firmware_rw_build_spec, spec used to determine the firmware RW build
78 test with a ChromeOS build.
79 firmware_ro_build_spec, spec used to determine the firmware RO build
80 test with a ChromeOS build.
Brigit Rossbachbb080912020-11-18 13:52:17 -070081 firmware_ro_version, pinned firmware RO version.
82 firmware_rw_version, pinned firmware RW version.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070083 test_source, the source of test code when firmware will be updated in
84 the test. The value can be 'firmware_rw', 'firmware_ro' or 'cros'.
85 job_retry, set to True to enable job-level retry. Default is False.
Xixuan Wu80531932017-10-12 17:26:51 -070086 no_delay, set to True to raise the priority of this task in task.
87 force, set to True to schedule this suite no matter whether there's
88 duplicate jobs before.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070089 queue, so the suite jobs can start running tests with no waiting.
90 hour, an integer specifying the hour that a nightly run should be
91 triggered, default is set to 21.
92 day, an integer specifying the day of a week that a weekly run should
93 be triggered, default is set to 5 (Saturday).
94 os_type, type of OS, e.g., cros, brillo, android. Default is cros.
95 The argument is required for android/brillo builds.
96 launch_control_branches, comma separated string of launch control
97 branches. The argument is required and only applicable for
98 android/brillo builds.
99 launch_control_targets, comma separated string of build targets for
100 launch control builds. The argument is required and only
101 applicable for android/brillo builds.
102 testbed_dut_count, number of duts to test when using a testbed.
Xinan Lin4757d6f2020-03-24 22:20:31 -0700103 qs_account, quota account for the unmanaged pool which has enabled
104 Quota Scheduler.
Jacob Kopczynskic54520f2020-08-07 13:20:12 -0700105
Xixuan Wu83118dd2018-08-27 12:11:35 -0700106 board_family_config: A board family dictionary mapping board_family name
107 to its corresponding boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700108 tot: The tot manager for checking ToT. If it's None, a new tot_manager
109 instance will be initialized.
Xinan Lin33937d62020-04-14 14:41:23 -0700110 is_sanity: A boolean; true if we are running in sanity env.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700111 """
Xixuan Wu5451a662017-10-17 10:57:40 -0700112 # Indicate whether there're suites get pushed into taskqueue for this task.
113 self.is_pushed = False
114
Taylor Clark8b06a832021-02-16 21:45:42 +0000115 self.analytics_name = task_info.analytics_name
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700116 self.branch_specs = task_info.branch_specs
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700117 self.cros_build_spec = task_info.cros_build_spec
Xixuan Wu008ee832017-10-12 16:59:34 -0700118 self.day = task_info.day
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700119 self.firmware_ro_build_spec = task_info.firmware_ro_build_spec
120 self.firmware_rw_build_spec = task_info.firmware_rw_build_spec
Brigit Rossbachbb080912020-11-18 13:52:17 -0700121 self.firmware_ro_version = task_info.firmware_ro_version
122 self.firmware_rw_version = task_info.firmware_rw_version
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700123 self.force = task_info.force
124 self.frontdoor = task_info.frontdoor
125 self.hour = task_info.hour
126 self.job_retry = task_info.job_retry
127 self.name = task_info.name
128 self.no_delay = task_info.no_delay
129 self.num = task_info.num
Craig Bergstrom58263d32018-04-26 14:11:35 -0600130 self.only_hwtest_sanity_required = task_info.only_hwtest_sanity_required
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700131 self.os_type = task_info.os_type
132 self.pool = task_info.pool
133 self.priority = task_info.priority
Xinan Lin4757d6f2020-03-24 22:20:31 -0700134 self.qs_account = task_info.qs_account
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700135 self.suite = task_info.suite
136 self.test_source = task_info.test_source
137 self.testbed_dut_count = task_info.testbed_dut_count
138 self.timeout = task_info.timeout
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700139
140 if task_info.lc_branches:
141 self.launch_control_branches = [
142 t.strip() for t in task_info.lc_branches.split(',')]
143 else:
144 self.launch_control_branches = []
145
146 if task_info.lc_targets:
147 self.launch_control_targets = [
148 t.strip() for t in task_info.lc_targets.split(',')]
149 else:
150 self.launch_control_targets = []
151
152 if task_info.boards:
153 self.boards = [t.strip() for t in task_info.boards.split(',')]
154 else:
155 self.boards = []
156
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700157 if task_info.exclude_boards:
158 self.exclude_boards = [
159 t.strip() for t in task_info.exclude_boards.split(',')]
160 else:
161 self.exclude_boards = []
162
Xixuan Wu89897182019-01-03 15:28:01 -0800163 if task_info.models:
164 self.models = [t.strip() for t in task_info.models.split(',')]
165 else:
166 self.models = []
167
168 if task_info.exclude_models:
169 self.exclude_models = [
170 t.strip() for t in task_info.exclude_models.split(',')]
171 else:
172 self.exclude_models = []
173
C Shapiro09108252019-08-01 14:52:52 -0500174 self.any_model = task_info.any_model
175
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700176 if task_info.board_families:
177 # Finetune the allowed boards list with board_families & boards.
178 families = [family.strip()
179 for family in task_info.board_families.split(',')]
180 for family in families:
181 self.boards += board_family_config.get(family, [])
Xixuan Wu89897182019-01-03 15:28:01 -0800182
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700183 if task_info.exclude_board_families:
184 # Finetune the disallowed boards list with exclude_board_families
185 # & exclude_boards.
186 families = [family.strip()
187 for family in task_info.exclude_board_families.split(',')]
188 for family in families:
189 self.exclude_boards += board_family_config.get(family, [])
190
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700191 if tot is None:
192 self.tot_manager = tot_manager.TotMilestoneManager()
193 else:
194 self.tot_manager = tot
195
Xinan Linc8647112020-02-04 16:45:56 -0800196 self.dimensions = task_info.dimensions
197
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700198 self._set_spec_compare_info()
Xinan Lin33937d62020-04-14 14:41:23 -0700199 # Sanity test does not have to upload metrics.
200 if not is_sanity:
201 self.job_section = analytics.ScheduleJobSection(task_info)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700202
Xinan Lin028f9582019-12-11 10:55:33 -0800203 def schedule(self, launch_control_builds, cros_builds_tuple,
204 firmware_builds, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700205 """Schedule the task by its settings.
206
207 Args:
208 launch_control_builds: the build dict for Android boards, see
209 return value of |get_launch_control_builds|.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600210 cros_builds_tuple: the two-tuple of build dicts for ChromeOS boards,
211 see return value of |get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800212 firmware_builds: a dict of firmware artifact, see return value of
213 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700214 configs: a config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700215
216 Raises:
217 SchedulingError: if tasks that should be scheduled fail to schedule.
Xixuan Wu5451a662017-10-17 10:57:40 -0700218
219 Returns:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700220 A boolean indicator; true if there were any suites related to this
221 task which got pushed into the suites queue.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700222 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700223 assert configs.lab_config is not None
Xixuan Wu5451a662017-10-17 10:57:40 -0700224 self.is_pushed = False
225
Craig Bergstrom58263d32018-04-26 14:11:35 -0600226 branch_builds, relaxed_builds = cros_builds_tuple
227 builds_dict = branch_builds
228 if self.only_hwtest_sanity_required:
Xinan Linae7d6372019-09-12 14:42:10 -0700229 builds_dict = _split_unibuilds(relaxed_builds, configs)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600230
Xinan Lindf0698a2020-02-05 22:38:11 -0800231 # Record all target boards and models into job section.
232 lab_config = configs.lab_config
233 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
234 boards = self.boards if self.boards else lab_config.get_cros_board_list()
235 for b in boards:
236 if self.exclude_boards and b in self.exclude_boards:
237 continue
238 self.job_section.add_board(b)
239 models = self.models or models_by_board.get(b, [])
240 for m in models:
241 if m and '%s_%s' % (b, m) not in self.exclude_models:
242 self.job_section.add_model(m)
243
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700244 logging.info('######## Scheduling task %s ########', self.name)
245 if self.os_type == build_lib.OS_TYPE_CROS:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600246 if not builds_dict:
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700247 logging.info('No CrOS build to run, skip running.')
248 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800249 self._schedule_cros_builds(builds_dict, firmware_builds, configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700250 else:
251 if not launch_control_builds:
252 logging.info('No Android build to run, skip running.')
253 else:
254 self._schedule_launch_control_builds(launch_control_builds)
Xinan Lindf0698a2020-02-05 22:38:11 -0800255 upload_result = False
256 try:
257 upload_result = self.job_section.upload()
258 # For any exceptions from BQ, only log it and move on.
259 except Exception as e: #pylint: disable=broad-except
260 logging.exception(str(e))
261 if not upload_result:
262 logging.warning('Failed to insert row: %r', self.job_section)
Xixuan Wu5451a662017-10-17 10:57:40 -0700263 return self.is_pushed
264
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700265 def _set_spec_compare_info(self):
266 """Set branch spec compare info for task for further check."""
267 self._bare_branches = []
268 self._version_equal_constraint = False
269 self._version_gte_constraint = False
270 self._version_lte_constraint = False
271
272 if not self.branch_specs:
273 # Any milestone is OK.
274 self._numeric_constraint = version.LooseVersion('0')
275 else:
276 self._numeric_constraint = None
277 for spec in self.branch_specs:
278 if 'tot' in spec.lower():
279 # Convert spec >=tot-1 to >=RXX.
280 tot_str = spec[spec.index('tot'):]
281 spec = spec.replace(
282 tot_str, self.tot_manager.convert_tot_spec(tot_str))
283
284 if spec.startswith('>='):
285 self._numeric_constraint = version.LooseVersion(
286 spec.lstrip('>=R'))
287 self._version_gte_constraint = True
288 elif spec.startswith('<='):
289 self._numeric_constraint = version.LooseVersion(
290 spec.lstrip('<=R'))
291 self._version_lte_constraint = True
292 elif spec.startswith('=='):
293 self._version_equal_constraint = True
294 self._numeric_constraint = version.LooseVersion(
295 spec.lstrip('==R'))
296 else:
297 self._bare_branches.append(spec)
298
299 def _fits_spec(self, branch):
300 """Check if a branch is deemed OK by this task's branch specs.
301
302 Will return whether a branch 'fits' the specifications stored in this task.
303
304 Examples:
305 Assuming tot=R40
306 t = Task('Name', 'suite', ['factory', '>=tot-1'])
307 t._fits_spec('factory') # True
308 t._fits_spec('40') # True
309 t._fits_spec('38') # False
310 t._fits_spec('firmware') # False
311
312 Args:
313 branch: the branch to check.
314
315 Returns:
316 True if branch 'fits' with stored specs, False otherwise.
317 """
318 if branch in build_lib.BARE_BRANCHES:
319 return branch in self._bare_branches
320
321 if self._numeric_constraint:
322 if self._version_equal_constraint:
323 return version.LooseVersion(branch) == self._numeric_constraint
324 elif self._version_gte_constraint:
325 return version.LooseVersion(branch) >= self._numeric_constraint
326 elif self._version_lte_constraint:
327 return version.LooseVersion(branch) <= self._numeric_constraint
328 else:
329 return version.LooseVersion(branch) >= self._numeric_constraint
330 else:
331 return False
332
Xinan Lin028f9582019-12-11 10:55:33 -0800333 def _get_firmware_build(self, spec, board, firmware_build_dict, lab_config):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700334 """Get the firmware build name to test with ChromeOS build.
335
336 Args:
337 spec: a string build spec for RO or RW firmware, eg. firmware,
338 cros. For RO firmware, the value can also be released_ro_X.
339 board: a string board against which this task will run suite job.
Xinan Lin028f9582019-12-11 10:55:33 -0800340 firmware_build_dict: a dict of firmware artifacts, see return value of
341 |base_event.get_firmware_builds|.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700342 lab_config: a config.LabConfig object, to read lab config file.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700343
344 Returns:
345 A string firmware build name.
346
347 Raises:
348 ValueError: if failing to get firmware from lab config file;
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700349 """
350 if not spec or spec == 'stable':
351 # TODO(crbug.com/577316): Query stable RO firmware.
352 logging.debug('%s RO firmware build is not supported.', spec)
353 return None
354
355 try:
Dhanya Ganeshf6014b72020-08-04 22:33:28 +0000356 if firmware_build_dict:
Xinan Lin028f9582019-12-11 10:55:33 -0800357 return firmware_build_dict.get((spec, board), None)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700358 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800359 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700360 except ValueError as e:
361 logging.warning('Failed to get firmware from lab config file'
362 'for spec %s, board %s: %s', spec, board, str(e))
363 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700364
C Shapiro7f24a002017-12-05 14:25:09 -0700365 def _push_suite(
366 self,
Xinan Lindf0698a2020-02-05 22:38:11 -0800367 task_id=None,
C Shapiro7f24a002017-12-05 14:25:09 -0700368 board=None,
369 model=None,
370 cros_build=None,
371 firmware_rw_build=None,
372 firmware_ro_build=None,
373 test_source_build=None,
374 launch_control_build=None,
Xinan Lin4757d6f2020-03-24 22:20:31 -0700375 run_prod_code=False):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700376 """Schedule suite job for the task by pushing suites to SuiteQueue.
377
378 Args:
Xinan Lindf0698a2020-02-05 22:38:11 -0800379 task_id: the id to track this task in exectuion.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700380 board: the board against which this suite job run.
C Shapiro7f24a002017-12-05 14:25:09 -0700381 model: the model name for unibuild.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700382 cros_build: the CrOS build of this suite job.
383 firmware_rw_build: Firmware RW build to run this suite job with.
384 firmware_ro_build: Firmware RO build to run this suite job with.
385 test_source_build: Test source build, used for server-side
386 packaging of this suite job.
387 launch_control_build: the launch control build of this suite job.
388 run_prod_code: If True, the suite will run the test code that lives
389 in prod aka the test code currently on the lab servers. If
390 False, the control files and test code for this suite run will
391 be retrieved from the build artifacts. Default is False.
392 """
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 = {
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700409 build_lib.BuildVersionKey.ANDROID_BUILD_VERSION: android_build,
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700410 build_lib.BuildVersionKey.CROS_VERSION: cros_build,
411 build_lib.BuildVersionKey.FW_RO_VERSION: firmware_ro_build,
412 build_lib.BuildVersionKey.FW_RW_VERSION: firmware_rw_build,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700413 build_lib.BuildVersionKey.TESTBED_BUILD_VERSION: testbed_build,
Taylor Clark8b06a832021-02-16 21:45:42 +0000414 'analytics_name': self.analytics_name,
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700415 'board': board,
416 'dimensions': self.dimensions,
417 'force': self.force,
418 'job_retry': self.job_retry,
419 'max_runtime_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
420 'model': model,
421 'name': self.name,
422 'no_delay': self.no_delay,
423 'no_wait_for_results': not self.job_retry,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700424 'num': self.num,
425 'pool': self.pool,
426 'priority': self.priority,
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700427 'qs_account': self.qs_account,
428 'run_prod_code': run_prod_code,
429 'suite': self.suite,
430 'task_id': task_id,
431 'test_source_build': test_source_build,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700432 'timeout': self.timeout,
433 'timeout_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700434 }
435
Xinan Lin9e4917d2019-11-04 10:58:47 -0800436 task_executor.push(task_executor.SUITES_QUEUE,
437 tag=self.suite,
438 **suite_job_parameters)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700439 logging.info('Pushing task %r into taskqueue', suite_job_parameters)
Xixuan Wu5451a662017-10-17 10:57:40 -0700440 self.is_pushed = True
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700441
Xinan Lin028f9582019-12-11 10:55:33 -0800442 def _schedule_cros_builds(self, build_dict, firmware_build_dict, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700443 """Schedule tasks with branch builds.
444
445 Args:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600446 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800447 value of |build_lib.get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800448 firmware_build_dict: a dict of firmware artifact, see return value of
449 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700450 configs: A config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700451 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700452 lab_config = configs.lab_config
C Shapiro7f24a002017-12-05 14:25:09 -0700453 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
C Shapiro09108252019-08-01 14:52:52 -0500454 model_agnostic_cros_builds = set()
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700455 for (board, passed_model, build_type,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600456 milestone), manifest in build_dict.iteritems():
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700457 cros_build = str(build_lib.CrOSBuild(board, build_type, milestone,
458 manifest))
459 logging.info('Running %s on %s', self.name, cros_build)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700460 if self.exclude_boards and board in self.exclude_boards:
461 logging.debug('Board %s is in excluded board list: %s',
462 board, self.exclude_boards)
463 continue
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700464
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700465 if self.boards and board not in self.boards:
466 logging.debug('Board %s is not in supported board list: %s',
467 board, self.boards)
468 continue
469
470 # Check the fitness of the build's branch for task
471 branch_build_spec = _pick_branch(build_type, milestone)
472 if not self._fits_spec(branch_build_spec):
Xinan Lindf0698a2020-02-05 22:38:11 -0800473 msg = ("branch_build spec %s doesn't fit this task's "
474 "requirement: %s") % (branch_build_spec,
475 ",".join(self.branch_specs))
476 logging.debug(msg)
477 self.job_section.add_schedule_job(board, passed_model, msg=msg)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700478 continue
479
Xinan Lindf0698a2020-02-05 22:38:11 -0800480 # Record this build as it matches both board and branch specs.
481 if self.only_hwtest_sanity_required:
482 self.job_section.add_matched_relax_build(
483 board, build_type, milestone, manifest)
484 else:
485 self.job_section.add_matched_build(
486 board, build_type, milestone, manifest)
487
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700488 firmware_rw_build = None
489 firmware_ro_build = None
Brigit Rossbachbb080912020-11-18 13:52:17 -0700490 if self.firmware_ro_version:
491 firmware_ro_build = self.firmware_ro_version
492
493 if self.firmware_rw_version:
494 firmware_rw_build = self.firmware_rw_version
495
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700496 if self.firmware_rw_build_spec or self.firmware_ro_build_spec:
497 firmware_rw_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800498 self.firmware_rw_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700499 firmware_ro_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800500 self.firmware_ro_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700501
502 if not firmware_ro_build and self.firmware_ro_build_spec:
Xinan Lindf0698a2020-02-05 22:38:11 -0800503 msg = 'No RO firmware ro build to run, skip running'
504 logging.debug(msg)
505 self.job_section.add_schedule_job(board, passed_model, msg=msg)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700506 continue
507
508 if self.test_source == build_lib.BuildType.FIRMWARE_RW:
509 test_source_build = firmware_rw_build
510 else:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700511 # Default test source build to CrOS build if it's not specified.
512 # Past versions chose based on run_prod_code, but we no longer respect
513 # that option and scheduler settings should always set it to False.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700514 test_source_build = cros_build
515
Xinan Lindf0698a2020-02-05 22:38:11 -0800516 # Record the matched firwmare build.
517 if firmware_rw_build:
518 self.job_section.add_matched_fw_build(
519 board,
520 self.firmware_rw_build_spec,
521 firmware_rw_build,
522 read_only=False)
523 if firmware_ro_build:
524 self.job_section.add_matched_fw_build(
525 board,
526 self.firmware_ro_build_spec,
527 firmware_ro_build,
528 read_only=True)
529
Xinan Lin6668e0f2020-05-29 10:02:57 -0700530 # Board above is used as build target to control the CrOS image.
531 # The following part is to assign models for lab boards, where
532 # the suffix should be removed.
Prathmesh Prabhu074b8d42020-08-13 17:17:33 +0000533 hwtest_board = build_lib.reshape_board(board)
534
Xinan Lin6668e0f2020-05-29 10:02:57 -0700535 models = models_by_board.get(hwtest_board, [None])
C Shapiro7f24a002017-12-05 14:25:09 -0700536
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000537 for model in models:
538 if ((passed_model is not None and model == passed_model) or
539 passed_model is None):
Xinan Lin6668e0f2020-05-29 10:02:57 -0700540 full_model_name = '%s_%s' % (hwtest_board, model)
Xixuan Wua41efa22019-05-17 14:28:04 -0700541 # Respect exclude first.
542 if self.exclude_models and full_model_name in self.exclude_models:
543 logging.debug("Skip model %s as it's in exclude model list %s",
544 model, self.exclude_models)
545 continue
546
547 if self.models and full_model_name not in self.models:
548 logging.debug("Skip model %s as it's not in support model list %s",
549 model, self.models)
550 continue
551
C Shapiro09108252019-08-01 14:52:52 -0500552 explicit_model = model
553
554 if self.any_model:
Xinan Lin3ba18a02019-08-13 15:44:55 -0700555 explicit_model = None
C Shapiro09108252019-08-01 14:52:52 -0500556 unique_build = str(cros_build)
557 if unique_build in model_agnostic_cros_builds:
558 # Skip since we've already run with no explicit model set.
Xinan Lindf0698a2020-02-05 22:38:11 -0800559 msg = "Skip model %s as any_model enabled for this job." % model
560 self.job_section.add_schedule_job(board, model, msg=msg)
C Shapiro09108252019-08-01 14:52:52 -0500561 continue
562 model_agnostic_cros_builds.add(unique_build)
563
Xinan Lindf0698a2020-02-05 22:38:11 -0800564 task_id = str(uuid.uuid1())
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000565 self._push_suite(
Xinan Lindf0698a2020-02-05 22:38:11 -0800566 task_id=task_id,
Xixuan Wub4b2f412019-05-03 11:22:31 -0700567 board=hwtest_board,
C Shapiro09108252019-08-01 14:52:52 -0500568 model=explicit_model,
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000569 cros_build=cros_build,
570 firmware_rw_build=firmware_rw_build,
571 firmware_ro_build=firmware_ro_build,
Xinan Lin0550f492020-01-21 16:25:53 -0800572 test_source_build=test_source_build)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700573
Xinan Lin6668e0f2020-05-29 10:02:57 -0700574 # Analytics table stores the build target instead of the lab board.
Xinan Lin0673a182020-04-14 15:09:35 -0700575 self.job_section.add_schedule_job(
576 board, explicit_model, task_id=task_id)
Xinan Lindf0698a2020-02-05 22:38:11 -0800577
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700578 def _schedule_launch_control_builds(self, launch_control_builds):
579 """Schedule tasks with launch control builds.
580
581 Args:
582 launch_control_builds: the build dict for Android boards.
583 """
584 for board, launch_control_build in launch_control_builds.iteritems():
585 logging.debug('Running %s on %s', self.name, board)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700586 if self.exclude_boards and board in self.exclude_boards:
587 logging.debug('Board %s is in excluded board list: %s',
588 board, self.exclude_boards)
589 continue
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700590 if self.boards and board not in self.boards:
591 logging.debug('Board %s is not in supported board list: %s',
592 board, self.boards)
593 continue
594
595 for android_build in launch_control_build:
596 if not any([branch in android_build
597 for branch in self.launch_control_branches]):
598 logging.debug('Branch %s is not required to run for task '
599 '%s', android_build, self.name)
600 continue
601
602 self._push_suite(board=board,
603 test_source_build=android_build,
604 launch_control_build=android_build)
605
606
607def _pick_branch(build_type, milestone):
608 """Select branch based on build type.
609
610 If the build_type is a bare branch, return build_type as the build spec.
611 If the build_type is a normal CrOS branch, return milestone as the build
612 spec.
613
614 Args:
615 build_type: a string builder name, like 'release'.
616 milestone: a string milestone, like '55'.
617
618 Returns:
619 A string milestone if build_type represents CrOS build, otherwise
620 return build_type.
621 """
622 return build_type if build_type in build_lib.BARE_BRANCHES else milestone
Xinan Linae7d6372019-09-12 14:42:10 -0700623
624
625def _split_unibuilds(build_dict, configs):
626 """Split the uni-builds to all models under a board.
627
628 Args:
629 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800630 value of |build_lib.get_cros_builds|.
Xinan Linae7d6372019-09-12 14:42:10 -0700631 configs: a config_reader.Configs object.
632
633 Returns:
634 A build dict.
635 """
636 models_by_board = configs.lab_config.get_cros_model_map()
637 if not models_by_board:
638 return build_dict
639 all_branch_build_dict = {}
640 for (board, model, config, milestone), platform in build_dict.iteritems():
641 uni_build_models = models_by_board.get(board)
642 if uni_build_models is not None and model is None:
643 for uni_build_model in uni_build_models:
644 model_key = (board, uni_build_model, config, milestone)
645 _add_build_dict(all_branch_build_dict, model_key, platform)
646 continue
647 build_key = (board, model, config, milestone)
648 _add_build_dict(all_branch_build_dict, build_key, platform)
649
650 return all_branch_build_dict
651
652
653def _add_build_dict(build_dict, key, value):
654 """A wrapper to add or update an item in build_dict."""
655 cur_manifest = build_dict.get(key)
656 if cur_manifest is None:
657 build_dict[key] = value
658 return
659 build_dict[key] = max(
660 [cur_manifest, value], key=version.LooseVersion)