blob: 02ed22aa1ae3f232f4ab173142e8468d4a97858a [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.
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.
Brigit Rossbachbb080912020-11-18 13:52:17 -070080 firmware_ro_version, pinned firmware RO version.
81 firmware_rw_version, pinned firmware RW version.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070082 test_source, the source of test code when firmware will be updated in
83 the test. The value can be 'firmware_rw', 'firmware_ro' or 'cros'.
84 job_retry, set to True to enable job-level retry. Default is False.
Xixuan Wu80531932017-10-12 17:26:51 -070085 no_delay, set to True to raise the priority of this task in task.
86 force, set to True to schedule this suite no matter whether there's
87 duplicate jobs before.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070088 queue, so the suite jobs can start running tests with no waiting.
89 hour, an integer specifying the hour that a nightly run should be
90 triggered, default is set to 21.
91 day, an integer specifying the day of a week that a weekly run should
92 be triggered, default is set to 5 (Saturday).
93 os_type, type of OS, e.g., cros, brillo, android. Default is cros.
94 The argument is required for android/brillo builds.
95 launch_control_branches, comma separated string of launch control
96 branches. The argument is required and only applicable for
97 android/brillo builds.
98 launch_control_targets, comma separated string of build targets for
99 launch control builds. The argument is required and only
100 applicable for android/brillo builds.
101 testbed_dut_count, number of duts to test when using a testbed.
Xinan Lin4757d6f2020-03-24 22:20:31 -0700102 qs_account, quota account for the unmanaged pool which has enabled
103 Quota Scheduler.
Jacob Kopczynskic54520f2020-08-07 13:20:12 -0700104
Xixuan Wu83118dd2018-08-27 12:11:35 -0700105 board_family_config: A board family dictionary mapping board_family name
106 to its corresponding boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700107 tot: The tot manager for checking ToT. If it's None, a new tot_manager
108 instance will be initialized.
Xinan Lin33937d62020-04-14 14:41:23 -0700109 is_sanity: A boolean; true if we are running in sanity env.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700110 """
Xixuan Wu5451a662017-10-17 10:57:40 -0700111 # Indicate whether there're suites get pushed into taskqueue for this task.
112 self.is_pushed = False
113
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700114 self.branch_specs = task_info.branch_specs
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700115 self.cros_build_spec = task_info.cros_build_spec
Xixuan Wu008ee832017-10-12 16:59:34 -0700116 self.day = task_info.day
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700117 self.firmware_ro_build_spec = task_info.firmware_ro_build_spec
118 self.firmware_rw_build_spec = task_info.firmware_rw_build_spec
Brigit Rossbachbb080912020-11-18 13:52:17 -0700119 self.firmware_ro_version = task_info.firmware_ro_version
120 self.firmware_rw_version = task_info.firmware_rw_version
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700121 self.force = task_info.force
122 self.frontdoor = task_info.frontdoor
123 self.hour = task_info.hour
124 self.job_retry = task_info.job_retry
125 self.name = task_info.name
126 self.no_delay = task_info.no_delay
127 self.num = task_info.num
Craig Bergstrom58263d32018-04-26 14:11:35 -0600128 self.only_hwtest_sanity_required = task_info.only_hwtest_sanity_required
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700129 self.os_type = task_info.os_type
130 self.pool = task_info.pool
131 self.priority = task_info.priority
Xinan Lin4757d6f2020-03-24 22:20:31 -0700132 self.qs_account = task_info.qs_account
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700133 self.suite = task_info.suite
134 self.test_source = task_info.test_source
135 self.testbed_dut_count = task_info.testbed_dut_count
136 self.timeout = task_info.timeout
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700137
138 if task_info.lc_branches:
139 self.launch_control_branches = [
140 t.strip() for t in task_info.lc_branches.split(',')]
141 else:
142 self.launch_control_branches = []
143
144 if task_info.lc_targets:
145 self.launch_control_targets = [
146 t.strip() for t in task_info.lc_targets.split(',')]
147 else:
148 self.launch_control_targets = []
149
150 if task_info.boards:
151 self.boards = [t.strip() for t in task_info.boards.split(',')]
152 else:
153 self.boards = []
154
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700155 if task_info.exclude_boards:
156 self.exclude_boards = [
157 t.strip() for t in task_info.exclude_boards.split(',')]
158 else:
159 self.exclude_boards = []
160
Xixuan Wu89897182019-01-03 15:28:01 -0800161 if task_info.models:
162 self.models = [t.strip() for t in task_info.models.split(',')]
163 else:
164 self.models = []
165
166 if task_info.exclude_models:
167 self.exclude_models = [
168 t.strip() for t in task_info.exclude_models.split(',')]
169 else:
170 self.exclude_models = []
171
C Shapiro09108252019-08-01 14:52:52 -0500172 self.any_model = task_info.any_model
173
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700174 if task_info.board_families:
175 # Finetune the allowed boards list with board_families & boards.
176 families = [family.strip()
177 for family in task_info.board_families.split(',')]
178 for family in families:
179 self.boards += board_family_config.get(family, [])
Xixuan Wu89897182019-01-03 15:28:01 -0800180
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700181 if task_info.exclude_board_families:
182 # Finetune the disallowed boards list with exclude_board_families
183 # & exclude_boards.
184 families = [family.strip()
185 for family in task_info.exclude_board_families.split(',')]
186 for family in families:
187 self.exclude_boards += board_family_config.get(family, [])
188
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700189 if tot is None:
190 self.tot_manager = tot_manager.TotMilestoneManager()
191 else:
192 self.tot_manager = tot
193
Xinan Linc8647112020-02-04 16:45:56 -0800194 self.dimensions = task_info.dimensions
195
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700196 self._set_spec_compare_info()
Xinan Lin33937d62020-04-14 14:41:23 -0700197 # Sanity test does not have to upload metrics.
198 if not is_sanity:
199 self.job_section = analytics.ScheduleJobSection(task_info)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700200
Xinan Lin028f9582019-12-11 10:55:33 -0800201 def schedule(self, launch_control_builds, cros_builds_tuple,
202 firmware_builds, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700203 """Schedule the task by its settings.
204
205 Args:
206 launch_control_builds: the build dict for Android boards, see
207 return value of |get_launch_control_builds|.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600208 cros_builds_tuple: the two-tuple of build dicts for ChromeOS boards,
209 see return value of |get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800210 firmware_builds: a dict of firmware artifact, see return value of
211 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700212 configs: a config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700213
214 Raises:
215 SchedulingError: if tasks that should be scheduled fail to schedule.
Xixuan Wu5451a662017-10-17 10:57:40 -0700216
217 Returns:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700218 A boolean indicator; true if there were any suites related to this
219 task which got pushed into the suites queue.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700220 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700221 assert configs.lab_config is not None
Xixuan Wu5451a662017-10-17 10:57:40 -0700222 self.is_pushed = False
223
Craig Bergstrom58263d32018-04-26 14:11:35 -0600224 branch_builds, relaxed_builds = cros_builds_tuple
225 builds_dict = branch_builds
226 if self.only_hwtest_sanity_required:
Xinan Linae7d6372019-09-12 14:42:10 -0700227 builds_dict = _split_unibuilds(relaxed_builds, configs)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600228
Xinan Lindf0698a2020-02-05 22:38:11 -0800229 # Record all target boards and models into job section.
230 lab_config = configs.lab_config
231 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
232 boards = self.boards if self.boards else lab_config.get_cros_board_list()
233 for b in boards:
234 if self.exclude_boards and b in self.exclude_boards:
235 continue
236 self.job_section.add_board(b)
237 models = self.models or models_by_board.get(b, [])
238 for m in models:
239 if m and '%s_%s' % (b, m) not in self.exclude_models:
240 self.job_section.add_model(m)
241
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700242 logging.info('######## Scheduling task %s ########', self.name)
243 if self.os_type == build_lib.OS_TYPE_CROS:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600244 if not builds_dict:
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700245 logging.info('No CrOS build to run, skip running.')
246 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800247 self._schedule_cros_builds(builds_dict, firmware_builds, configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700248 else:
249 if not launch_control_builds:
250 logging.info('No Android build to run, skip running.')
251 else:
252 self._schedule_launch_control_builds(launch_control_builds)
Xinan Lindf0698a2020-02-05 22:38:11 -0800253 upload_result = False
254 try:
255 upload_result = self.job_section.upload()
256 # For any exceptions from BQ, only log it and move on.
257 except Exception as e: #pylint: disable=broad-except
258 logging.exception(str(e))
259 if not upload_result:
260 logging.warning('Failed to insert row: %r', self.job_section)
Xixuan Wu5451a662017-10-17 10:57:40 -0700261 return self.is_pushed
262
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700263 def _set_spec_compare_info(self):
264 """Set branch spec compare info for task for further check."""
265 self._bare_branches = []
266 self._version_equal_constraint = False
267 self._version_gte_constraint = False
268 self._version_lte_constraint = False
269
270 if not self.branch_specs:
271 # Any milestone is OK.
272 self._numeric_constraint = version.LooseVersion('0')
273 else:
274 self._numeric_constraint = None
275 for spec in self.branch_specs:
276 if 'tot' in spec.lower():
277 # Convert spec >=tot-1 to >=RXX.
278 tot_str = spec[spec.index('tot'):]
279 spec = spec.replace(
280 tot_str, self.tot_manager.convert_tot_spec(tot_str))
281
282 if spec.startswith('>='):
283 self._numeric_constraint = version.LooseVersion(
284 spec.lstrip('>=R'))
285 self._version_gte_constraint = True
286 elif spec.startswith('<='):
287 self._numeric_constraint = version.LooseVersion(
288 spec.lstrip('<=R'))
289 self._version_lte_constraint = True
290 elif spec.startswith('=='):
291 self._version_equal_constraint = True
292 self._numeric_constraint = version.LooseVersion(
293 spec.lstrip('==R'))
294 else:
295 self._bare_branches.append(spec)
296
297 def _fits_spec(self, branch):
298 """Check if a branch is deemed OK by this task's branch specs.
299
300 Will return whether a branch 'fits' the specifications stored in this task.
301
302 Examples:
303 Assuming tot=R40
304 t = Task('Name', 'suite', ['factory', '>=tot-1'])
305 t._fits_spec('factory') # True
306 t._fits_spec('40') # True
307 t._fits_spec('38') # False
308 t._fits_spec('firmware') # False
309
310 Args:
311 branch: the branch to check.
312
313 Returns:
314 True if branch 'fits' with stored specs, False otherwise.
315 """
316 if branch in build_lib.BARE_BRANCHES:
317 return branch in self._bare_branches
318
319 if self._numeric_constraint:
320 if self._version_equal_constraint:
321 return version.LooseVersion(branch) == self._numeric_constraint
322 elif self._version_gte_constraint:
323 return version.LooseVersion(branch) >= self._numeric_constraint
324 elif self._version_lte_constraint:
325 return version.LooseVersion(branch) <= self._numeric_constraint
326 else:
327 return version.LooseVersion(branch) >= self._numeric_constraint
328 else:
329 return False
330
Xinan Lin028f9582019-12-11 10:55:33 -0800331 def _get_firmware_build(self, spec, board, firmware_build_dict, lab_config):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700332 """Get the firmware build name to test with ChromeOS build.
333
334 Args:
335 spec: a string build spec for RO or RW firmware, eg. firmware,
336 cros. For RO firmware, the value can also be released_ro_X.
337 board: a string board against which this task will run suite job.
Xinan Lin028f9582019-12-11 10:55:33 -0800338 firmware_build_dict: a dict of firmware artifacts, see return value of
339 |base_event.get_firmware_builds|.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700340 lab_config: a config.LabConfig object, to read lab config file.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700341
342 Returns:
343 A string firmware build name.
344
345 Raises:
346 ValueError: if failing to get firmware from lab config file;
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700347 """
348 if not spec or spec == 'stable':
349 # TODO(crbug.com/577316): Query stable RO firmware.
350 logging.debug('%s RO firmware build is not supported.', spec)
351 return None
352
353 try:
Dhanya Ganeshf6014b72020-08-04 22:33:28 +0000354 if firmware_build_dict:
Xinan Lin028f9582019-12-11 10:55:33 -0800355 return firmware_build_dict.get((spec, board), None)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700356 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800357 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700358 except ValueError as e:
359 logging.warning('Failed to get firmware from lab config file'
360 'for spec %s, board %s: %s', spec, board, str(e))
361 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700362
C Shapiro7f24a002017-12-05 14:25:09 -0700363 def _push_suite(
364 self,
Xinan Lindf0698a2020-02-05 22:38:11 -0800365 task_id=None,
C Shapiro7f24a002017-12-05 14:25:09 -0700366 board=None,
367 model=None,
368 cros_build=None,
369 firmware_rw_build=None,
370 firmware_ro_build=None,
371 test_source_build=None,
372 launch_control_build=None,
Xinan Lin4757d6f2020-03-24 22:20:31 -0700373 run_prod_code=False):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700374 """Schedule suite job for the task by pushing suites to SuiteQueue.
375
376 Args:
Xinan Lindf0698a2020-02-05 22:38:11 -0800377 task_id: the id to track this task in exectuion.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700378 board: the board against which this suite job run.
C Shapiro7f24a002017-12-05 14:25:09 -0700379 model: the model name for unibuild.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700380 cros_build: the CrOS build of this suite job.
381 firmware_rw_build: Firmware RW build to run this suite job with.
382 firmware_ro_build: Firmware RO build to run this suite job with.
383 test_source_build: Test source build, used for server-side
384 packaging of this suite job.
385 launch_control_build: the launch control build of this suite job.
386 run_prod_code: If True, the suite will run the test code that lives
387 in prod aka the test code currently on the lab servers. If
388 False, the control files and test code for this suite run will
389 be retrieved from the build artifacts. Default is False.
390 """
391 android_build = None
392 testbed_build = None
393
394 if self.testbed_dut_count:
395 launch_control_build = '%s#%d' % (launch_control_build,
396 self.testbed_dut_count)
397 test_source_build = launch_control_build
398 board = '%s-%d' % (board, self.testbed_dut_count)
399
400 if launch_control_build:
401 if not self.testbed_dut_count:
402 android_build = launch_control_build
403 else:
404 testbed_build = launch_control_build
405
406 suite_job_parameters = {
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700407 build_lib.BuildVersionKey.ANDROID_BUILD_VERSION: android_build,
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700408 build_lib.BuildVersionKey.CROS_VERSION: cros_build,
409 build_lib.BuildVersionKey.FW_RO_VERSION: firmware_ro_build,
410 build_lib.BuildVersionKey.FW_RW_VERSION: firmware_rw_build,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700411 build_lib.BuildVersionKey.TESTBED_BUILD_VERSION: testbed_build,
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700412 'board': board,
413 'dimensions': self.dimensions,
414 'force': self.force,
415 'job_retry': self.job_retry,
416 'max_runtime_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
417 'model': model,
418 'name': self.name,
419 'no_delay': self.no_delay,
420 'no_wait_for_results': not self.job_retry,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700421 'num': self.num,
422 'pool': self.pool,
423 'priority': self.priority,
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700424 'qs_account': self.qs_account,
425 'run_prod_code': run_prod_code,
426 'suite': self.suite,
427 'task_id': task_id,
428 'test_source_build': test_source_build,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700429 'timeout': self.timeout,
430 'timeout_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700431 }
432
Xinan Lin9e4917d2019-11-04 10:58:47 -0800433 task_executor.push(task_executor.SUITES_QUEUE,
434 tag=self.suite,
435 **suite_job_parameters)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700436 logging.info('Pushing task %r into taskqueue', suite_job_parameters)
Xixuan Wu5451a662017-10-17 10:57:40 -0700437 self.is_pushed = True
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700438
Xinan Lin028f9582019-12-11 10:55:33 -0800439 def _schedule_cros_builds(self, build_dict, firmware_build_dict, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700440 """Schedule tasks with branch builds.
441
442 Args:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600443 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800444 value of |build_lib.get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800445 firmware_build_dict: a dict of firmware artifact, see return value of
446 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700447 configs: A config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700448 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700449 lab_config = configs.lab_config
C Shapiro7f24a002017-12-05 14:25:09 -0700450 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
C Shapiro09108252019-08-01 14:52:52 -0500451 model_agnostic_cros_builds = set()
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700452 for (board, passed_model, build_type,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600453 milestone), manifest in build_dict.iteritems():
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700454 cros_build = str(build_lib.CrOSBuild(board, build_type, milestone,
455 manifest))
456 logging.info('Running %s on %s', self.name, cros_build)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700457 if self.exclude_boards and board in self.exclude_boards:
458 logging.debug('Board %s is in excluded board list: %s',
459 board, self.exclude_boards)
460 continue
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700461
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700462 if self.boards and board not in self.boards:
463 logging.debug('Board %s is not in supported board list: %s',
464 board, self.boards)
465 continue
466
467 # Check the fitness of the build's branch for task
468 branch_build_spec = _pick_branch(build_type, milestone)
469 if not self._fits_spec(branch_build_spec):
Xinan Lindf0698a2020-02-05 22:38:11 -0800470 msg = ("branch_build spec %s doesn't fit this task's "
471 "requirement: %s") % (branch_build_spec,
472 ",".join(self.branch_specs))
473 logging.debug(msg)
474 self.job_section.add_schedule_job(board, passed_model, msg=msg)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700475 continue
476
Xinan Lindf0698a2020-02-05 22:38:11 -0800477 # Record this build as it matches both board and branch specs.
478 if self.only_hwtest_sanity_required:
479 self.job_section.add_matched_relax_build(
480 board, build_type, milestone, manifest)
481 else:
482 self.job_section.add_matched_build(
483 board, build_type, milestone, manifest)
484
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700485 firmware_rw_build = None
486 firmware_ro_build = None
Brigit Rossbachbb080912020-11-18 13:52:17 -0700487 if self.firmware_ro_version:
488 firmware_ro_build = self.firmware_ro_version
489
490 if self.firmware_rw_version:
491 firmware_rw_build = self.firmware_rw_version
492
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700493 if self.firmware_rw_build_spec or self.firmware_ro_build_spec:
494 firmware_rw_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800495 self.firmware_rw_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700496 firmware_ro_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800497 self.firmware_ro_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700498
499 if not firmware_ro_build and self.firmware_ro_build_spec:
Xinan Lindf0698a2020-02-05 22:38:11 -0800500 msg = 'No RO firmware ro build to run, skip running'
501 logging.debug(msg)
502 self.job_section.add_schedule_job(board, passed_model, msg=msg)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700503 continue
504
505 if self.test_source == build_lib.BuildType.FIRMWARE_RW:
506 test_source_build = firmware_rw_build
507 else:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700508 # Default test source build to CrOS build if it's not specified.
509 # Past versions chose based on run_prod_code, but we no longer respect
510 # that option and scheduler settings should always set it to False.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700511 test_source_build = cros_build
512
Xinan Lindf0698a2020-02-05 22:38:11 -0800513 # Record the matched firwmare build.
514 if firmware_rw_build:
515 self.job_section.add_matched_fw_build(
516 board,
517 self.firmware_rw_build_spec,
518 firmware_rw_build,
519 read_only=False)
520 if firmware_ro_build:
521 self.job_section.add_matched_fw_build(
522 board,
523 self.firmware_ro_build_spec,
524 firmware_ro_build,
525 read_only=True)
526
Xinan Lin6668e0f2020-05-29 10:02:57 -0700527 # Board above is used as build target to control the CrOS image.
528 # The following part is to assign models for lab boards, where
529 # the suffix should be removed.
Prathmesh Prabhu074b8d42020-08-13 17:17:33 +0000530 hwtest_board = build_lib.reshape_board(board)
531
Xinan Lin6668e0f2020-05-29 10:02:57 -0700532 models = models_by_board.get(hwtest_board, [None])
C Shapiro7f24a002017-12-05 14:25:09 -0700533
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000534 for model in models:
535 if ((passed_model is not None and model == passed_model) or
536 passed_model is None):
Xinan Lin6668e0f2020-05-29 10:02:57 -0700537 full_model_name = '%s_%s' % (hwtest_board, model)
Xixuan Wua41efa22019-05-17 14:28:04 -0700538 # Respect exclude first.
539 if self.exclude_models and full_model_name in self.exclude_models:
540 logging.debug("Skip model %s as it's in exclude model list %s",
541 model, self.exclude_models)
542 continue
543
544 if self.models and full_model_name not in self.models:
545 logging.debug("Skip model %s as it's not in support model list %s",
546 model, self.models)
547 continue
548
C Shapiro09108252019-08-01 14:52:52 -0500549 explicit_model = model
550
551 if self.any_model:
Xinan Lin3ba18a02019-08-13 15:44:55 -0700552 explicit_model = None
C Shapiro09108252019-08-01 14:52:52 -0500553 unique_build = str(cros_build)
554 if unique_build in model_agnostic_cros_builds:
555 # Skip since we've already run with no explicit model set.
Xinan Lindf0698a2020-02-05 22:38:11 -0800556 msg = "Skip model %s as any_model enabled for this job." % model
557 self.job_section.add_schedule_job(board, model, msg=msg)
C Shapiro09108252019-08-01 14:52:52 -0500558 continue
559 model_agnostic_cros_builds.add(unique_build)
560
Xinan Lindf0698a2020-02-05 22:38:11 -0800561 task_id = str(uuid.uuid1())
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000562 self._push_suite(
Xinan Lindf0698a2020-02-05 22:38:11 -0800563 task_id=task_id,
Xixuan Wub4b2f412019-05-03 11:22:31 -0700564 board=hwtest_board,
C Shapiro09108252019-08-01 14:52:52 -0500565 model=explicit_model,
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000566 cros_build=cros_build,
567 firmware_rw_build=firmware_rw_build,
568 firmware_ro_build=firmware_ro_build,
Xinan Lin0550f492020-01-21 16:25:53 -0800569 test_source_build=test_source_build)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700570
Xinan Lin6668e0f2020-05-29 10:02:57 -0700571 # Analytics table stores the build target instead of the lab board.
Xinan Lin0673a182020-04-14 15:09:35 -0700572 self.job_section.add_schedule_job(
573 board, explicit_model, task_id=task_id)
Xinan Lindf0698a2020-02-05 22:38:11 -0800574
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700575 def _schedule_launch_control_builds(self, launch_control_builds):
576 """Schedule tasks with launch control builds.
577
578 Args:
579 launch_control_builds: the build dict for Android boards.
580 """
581 for board, launch_control_build in launch_control_builds.iteritems():
582 logging.debug('Running %s on %s', self.name, board)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700583 if self.exclude_boards and board in self.exclude_boards:
584 logging.debug('Board %s is in excluded board list: %s',
585 board, self.exclude_boards)
586 continue
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700587 if self.boards and board not in self.boards:
588 logging.debug('Board %s is not in supported board list: %s',
589 board, self.boards)
590 continue
591
592 for android_build in launch_control_build:
593 if not any([branch in android_build
594 for branch in self.launch_control_branches]):
595 logging.debug('Branch %s is not required to run for task '
596 '%s', android_build, self.name)
597 continue
598
599 self._push_suite(board=board,
600 test_source_build=android_build,
601 launch_control_build=android_build)
602
603
604def _pick_branch(build_type, milestone):
605 """Select branch based on build type.
606
607 If the build_type is a bare branch, return build_type as the build spec.
608 If the build_type is a normal CrOS branch, return milestone as the build
609 spec.
610
611 Args:
612 build_type: a string builder name, like 'release'.
613 milestone: a string milestone, like '55'.
614
615 Returns:
616 A string milestone if build_type represents CrOS build, otherwise
617 return build_type.
618 """
619 return build_type if build_type in build_lib.BARE_BRANCHES else milestone
Xinan Linae7d6372019-09-12 14:42:10 -0700620
621
622def _split_unibuilds(build_dict, configs):
623 """Split the uni-builds to all models under a board.
624
625 Args:
626 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800627 value of |build_lib.get_cros_builds|.
Xinan Linae7d6372019-09-12 14:42:10 -0700628 configs: a config_reader.Configs object.
629
630 Returns:
631 A build dict.
632 """
633 models_by_board = configs.lab_config.get_cros_model_map()
634 if not models_by_board:
635 return build_dict
636 all_branch_build_dict = {}
637 for (board, model, config, milestone), platform in build_dict.iteritems():
638 uni_build_models = models_by_board.get(board)
639 if uni_build_models is not None and model is None:
640 for uni_build_model in uni_build_models:
641 model_key = (board, uni_build_model, config, milestone)
642 _add_build_dict(all_branch_build_dict, model_key, platform)
643 continue
644 build_key = (board, model, config, milestone)
645 _add_build_dict(all_branch_build_dict, build_key, platform)
646
647 return all_branch_build_dict
648
649
650def _add_build_dict(build_dict, key, value):
651 """A wrapper to add or update an item in build_dict."""
652 cur_manifest = build_dict.get(key)
653 if cur_manifest is None:
654 build_dict[key] = value
655 return
656 build_dict[key] = max(
657 [cur_manifest, value], key=version.LooseVersion)