blob: b4121a63455870acb18553ec05492d5937d87710 [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
20# One kind of formats for RO firmware spec.
21_RELEASE_RO_FIRMWARE_SPEC = 'released_ro_'
22
23
24class SchedulingError(Exception):
25 """Raised to indicate a failure in scheduling a task."""
26
27
28class Task(object):
29 """Represents an entry from the suite_scheduler config file.
30
31 Each entry from the suite_scheduler config file maps one-to-one to a
32 Task. Each instance has enough information to schedule itself.
33 """
34
Po-Hsien Wangdd833072018-08-16 18:09:20 -070035 def __init__(self, task_info, board_family_config={}, tot=None):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070036 """Initialize a task instance.
37
38 Args:
39 task_info: a config_reader.TaskInfo object, which includes:
40 name, name of this task, e.g. 'NightlyPower'
41 suite, the name of the suite to run, e.g. 'graphics_per-day'
42 branch_specs, a pre-vetted iterable of branch specifiers,
43 e.g. ['>=R18', 'factory']
44 pool, the pool of machines to schedule tasks. Default is None.
45 num, the number of devices to shard the test suite. It could
46 be an Integer or None. By default it's None.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070047 board_families, a common separated list of board family to run this
48 task on. Boards belong to one of the board family in this list
49 would be added to task_info.boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070050 boards, a comma separated list of boards to run this task on. Default
Po-Hsien Wangdd833072018-08-16 18:09:20 -070051 is None, which allows this task to run on all boards. If same board
52 is specified in 'boards' and 'exclude_boards', we exclude this
53 board.
Xinan Linc8647112020-02-04 16:45:56 -080054 dimensions, a comma separated lists of labels. Each label is in
55 the form of 'key:value'.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070056 exclude_board_families, a common separated list of board family not to
57 run task on. Boards belong to one of the board family in this list
58 would be added to task_info.exclude_boards.
Po-Hsien Wang6d589732018-05-15 17:19:34 -070059 exclude_boards, a comma separated list of boards not to run this task
60 on. Default is None, which allows this task to run on all boards.
Po-Hsien Wangdd833072018-08-16 18:09:20 -070061 If same board is specified in 'boards' and 'exclude_boards', we
62 exclude this board.
Xixuan Wu89897182019-01-03 15:28:01 -080063 models, a comma separated list of models to run this task on. Default
64 is None, which allows this task to run on all models. If same model
65 is specified in 'models' and 'exclude_models', we exclude this
66 model.
67 exclude_models, a comma separated list of models not to run this task
68 on. Default is None, which allows this task to run on all models.
C Shapiro09108252019-08-01 14:52:52 -050069 any_model, set to True to not pass the model parameter and allow
70 a test suite to run any/all models available for testing.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070071 priority, the string name of a priority from constants.Priorities.
72 timeout, the max lifetime of the suite in hours.
73 cros_build_spec, spec used to determine the ChromeOS build to test
74 with a firmware build, e.g., tot, R41 etc.
75 firmware_rw_build_spec, spec used to determine the firmware RW build
76 test with a ChromeOS build.
77 firmware_ro_build_spec, spec used to determine the firmware RO build
78 test with a ChromeOS build.
79 test_source, the source of test code when firmware will be updated in
80 the test. The value can be 'firmware_rw', 'firmware_ro' or 'cros'.
81 job_retry, set to True to enable job-level retry. Default is False.
Xixuan Wu80531932017-10-12 17:26:51 -070082 no_delay, set to True to raise the priority of this task in task.
83 force, set to True to schedule this suite no matter whether there's
84 duplicate jobs before.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070085 queue, so the suite jobs can start running tests with no waiting.
86 hour, an integer specifying the hour that a nightly run should be
87 triggered, default is set to 21.
88 day, an integer specifying the day of a week that a weekly run should
89 be triggered, default is set to 5 (Saturday).
90 os_type, type of OS, e.g., cros, brillo, android. Default is cros.
91 The argument is required for android/brillo builds.
92 launch_control_branches, comma separated string of launch control
93 branches. The argument is required and only applicable for
94 android/brillo builds.
95 launch_control_targets, comma separated string of build targets for
96 launch control builds. The argument is required and only
97 applicable for android/brillo builds.
98 testbed_dut_count, number of duts to test when using a testbed.
Xinan Lin4757d6f2020-03-24 22:20:31 -070099 qs_account, quota account for the unmanaged pool which has enabled
100 Quota Scheduler.
Xixuan Wu83118dd2018-08-27 12:11:35 -0700101 board_family_config: A board family dictionary mapping board_family name
102 to its corresponding boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700103 tot: The tot manager for checking ToT. If it's None, a new tot_manager
104 instance will be initialized.
105 """
Xixuan Wu5451a662017-10-17 10:57:40 -0700106 # Indicate whether there're suites get pushed into taskqueue for this task.
107 self.is_pushed = False
108
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700109 self.name = task_info.name
110 self.suite = task_info.suite
111 self.branch_specs = task_info.branch_specs
112 self.pool = task_info.pool
113 self.num = task_info.num
114 self.priority = task_info.priority
115 self.timeout = task_info.timeout
116 self.cros_build_spec = task_info.cros_build_spec
117 self.firmware_rw_build_spec = task_info.firmware_rw_build_spec
118 self.firmware_ro_build_spec = task_info.firmware_ro_build_spec
119 self.test_source = task_info.test_source
120 self.job_retry = task_info.job_retry
121 self.no_delay = task_info.no_delay
Xixuan Wu80531932017-10-12 17:26:51 -0700122 self.force = task_info.force
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700123 self.os_type = task_info.os_type
Xinan Lin3ba18a02019-08-13 15:44:55 -0700124 self.frontdoor = task_info.frontdoor
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700125 self.testbed_dut_count = task_info.testbed_dut_count
Xixuan Wu008ee832017-10-12 16:59:34 -0700126 self.hour = task_info.hour
127 self.day = task_info.day
Craig Bergstrom58263d32018-04-26 14:11:35 -0600128 self.only_hwtest_sanity_required = task_info.only_hwtest_sanity_required
Xinan Lin4757d6f2020-03-24 22:20:31 -0700129 self.qs_account = task_info.qs_account
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700130
131 if task_info.lc_branches:
132 self.launch_control_branches = [
133 t.strip() for t in task_info.lc_branches.split(',')]
134 else:
135 self.launch_control_branches = []
136
137 if task_info.lc_targets:
138 self.launch_control_targets = [
139 t.strip() for t in task_info.lc_targets.split(',')]
140 else:
141 self.launch_control_targets = []
142
143 if task_info.boards:
144 self.boards = [t.strip() for t in task_info.boards.split(',')]
145 else:
146 self.boards = []
147
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700148 if task_info.exclude_boards:
149 self.exclude_boards = [
150 t.strip() for t in task_info.exclude_boards.split(',')]
151 else:
152 self.exclude_boards = []
153
Xixuan Wu89897182019-01-03 15:28:01 -0800154 if task_info.models:
155 self.models = [t.strip() for t in task_info.models.split(',')]
156 else:
157 self.models = []
158
159 if task_info.exclude_models:
160 self.exclude_models = [
161 t.strip() for t in task_info.exclude_models.split(',')]
162 else:
163 self.exclude_models = []
164
C Shapiro09108252019-08-01 14:52:52 -0500165 self.any_model = task_info.any_model
166
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700167 if task_info.board_families:
168 # Finetune the allowed boards list with board_families & boards.
169 families = [family.strip()
170 for family in task_info.board_families.split(',')]
171 for family in families:
172 self.boards += board_family_config.get(family, [])
Xixuan Wu89897182019-01-03 15:28:01 -0800173
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700174 if task_info.exclude_board_families:
175 # Finetune the disallowed boards list with exclude_board_families
176 # & exclude_boards.
177 families = [family.strip()
178 for family in task_info.exclude_board_families.split(',')]
179 for family in families:
180 self.exclude_boards += board_family_config.get(family, [])
181
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700182 if tot is None:
183 self.tot_manager = tot_manager.TotMilestoneManager()
184 else:
185 self.tot_manager = tot
186
Xinan Linc8647112020-02-04 16:45:56 -0800187 self.dimensions = task_info.dimensions
188
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700189 self._set_spec_compare_info()
Xinan Lindf0698a2020-02-05 22:38:11 -0800190 self.job_section = analytics.ScheduleJobSection(task_info)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700191
Xinan Lin028f9582019-12-11 10:55:33 -0800192 def schedule(self, launch_control_builds, cros_builds_tuple,
193 firmware_builds, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700194 """Schedule the task by its settings.
195
196 Args:
197 launch_control_builds: the build dict for Android boards, see
198 return value of |get_launch_control_builds|.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600199 cros_builds_tuple: the two-tuple of build dicts for ChromeOS boards,
200 see return value of |get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800201 firmware_builds: a dict of firmware artifact, see return value of
202 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700203 configs: a config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700204
205 Raises:
206 SchedulingError: if tasks that should be scheduled fail to schedule.
Xixuan Wu5451a662017-10-17 10:57:40 -0700207
208 Returns:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700209 A boolean indicator; true if there were any suites related to this
210 task which got pushed into the suites queue.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700211 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700212 assert configs.lab_config is not None
Xixuan Wu5451a662017-10-17 10:57:40 -0700213 self.is_pushed = False
214
Craig Bergstrom58263d32018-04-26 14:11:35 -0600215 branch_builds, relaxed_builds = cros_builds_tuple
216 builds_dict = branch_builds
217 if self.only_hwtest_sanity_required:
Xinan Linae7d6372019-09-12 14:42:10 -0700218 builds_dict = _split_unibuilds(relaxed_builds, configs)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600219
Xinan Lindf0698a2020-02-05 22:38:11 -0800220 # Record all target boards and models into job section.
221 lab_config = configs.lab_config
222 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
223 boards = self.boards if self.boards else lab_config.get_cros_board_list()
224 for b in boards:
225 if self.exclude_boards and b in self.exclude_boards:
226 continue
227 self.job_section.add_board(b)
228 models = self.models or models_by_board.get(b, [])
229 for m in models:
230 if m and '%s_%s' % (b, m) not in self.exclude_models:
231 self.job_section.add_model(m)
232
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700233 logging.info('######## Scheduling task %s ########', self.name)
234 if self.os_type == build_lib.OS_TYPE_CROS:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600235 if not builds_dict:
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700236 logging.info('No CrOS build to run, skip running.')
237 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800238 self._schedule_cros_builds(builds_dict, firmware_builds, configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700239 else:
240 if not launch_control_builds:
241 logging.info('No Android build to run, skip running.')
242 else:
243 self._schedule_launch_control_builds(launch_control_builds)
Xinan Lindf0698a2020-02-05 22:38:11 -0800244 upload_result = False
245 try:
246 upload_result = self.job_section.upload()
247 # For any exceptions from BQ, only log it and move on.
248 except Exception as e: #pylint: disable=broad-except
249 logging.exception(str(e))
250 if not upload_result:
251 logging.warning('Failed to insert row: %r', self.job_section)
Xixuan Wu5451a662017-10-17 10:57:40 -0700252 return self.is_pushed
253
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700254 def _set_spec_compare_info(self):
255 """Set branch spec compare info for task for further check."""
256 self._bare_branches = []
257 self._version_equal_constraint = False
258 self._version_gte_constraint = False
259 self._version_lte_constraint = False
260
261 if not self.branch_specs:
262 # Any milestone is OK.
263 self._numeric_constraint = version.LooseVersion('0')
264 else:
265 self._numeric_constraint = None
266 for spec in self.branch_specs:
267 if 'tot' in spec.lower():
268 # Convert spec >=tot-1 to >=RXX.
269 tot_str = spec[spec.index('tot'):]
270 spec = spec.replace(
271 tot_str, self.tot_manager.convert_tot_spec(tot_str))
272
273 if spec.startswith('>='):
274 self._numeric_constraint = version.LooseVersion(
275 spec.lstrip('>=R'))
276 self._version_gte_constraint = True
277 elif spec.startswith('<='):
278 self._numeric_constraint = version.LooseVersion(
279 spec.lstrip('<=R'))
280 self._version_lte_constraint = True
281 elif spec.startswith('=='):
282 self._version_equal_constraint = True
283 self._numeric_constraint = version.LooseVersion(
284 spec.lstrip('==R'))
285 else:
286 self._bare_branches.append(spec)
287
288 def _fits_spec(self, branch):
289 """Check if a branch is deemed OK by this task's branch specs.
290
291 Will return whether a branch 'fits' the specifications stored in this task.
292
293 Examples:
294 Assuming tot=R40
295 t = Task('Name', 'suite', ['factory', '>=tot-1'])
296 t._fits_spec('factory') # True
297 t._fits_spec('40') # True
298 t._fits_spec('38') # False
299 t._fits_spec('firmware') # False
300
301 Args:
302 branch: the branch to check.
303
304 Returns:
305 True if branch 'fits' with stored specs, False otherwise.
306 """
307 if branch in build_lib.BARE_BRANCHES:
308 return branch in self._bare_branches
309
310 if self._numeric_constraint:
311 if self._version_equal_constraint:
312 return version.LooseVersion(branch) == self._numeric_constraint
313 elif self._version_gte_constraint:
314 return version.LooseVersion(branch) >= self._numeric_constraint
315 elif self._version_lte_constraint:
316 return version.LooseVersion(branch) <= self._numeric_constraint
317 else:
318 return version.LooseVersion(branch) >= self._numeric_constraint
319 else:
320 return False
321
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700322 def _get_latest_firmware_build_from_lab_config(self, spec, board,
323 lab_config):
324 """Get latest firmware from lab config file.
325
326 Args:
327 spec: a string build spec for RO or RW firmware, eg. firmware,
328 cros. For RO firmware, the value can also be released_ro_X.
329 board: a string board against which this task will run suite job.
330 lab_config: a config.LabConfig object, to read lab config file.
331
332 Returns:
333 A string latest firmware build.
334
335 Raises:
336 ValueError: if no firmware build list is found in lab config file.
337 """
338 index = int(spec[len(_RELEASE_RO_FIRMWARE_SPEC):])
339 released_ro_builds = lab_config.get_firmware_ro_build_list(
340 'RELEASED_RO_BUILDS_%s' % board).split(',')
341 if len(released_ro_builds) < index:
342 raise ValueError('No %dth ro_builds in the lab_config firmware '
343 'list %r' % (index, released_ro_builds))
344
345 logging.debug('Get ro_build: %s', released_ro_builds[index - 1])
346 return released_ro_builds[index - 1]
347
Xinan Lin028f9582019-12-11 10:55:33 -0800348 def _get_firmware_build(self, spec, board, firmware_build_dict, lab_config):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700349 """Get the firmware build name to test with ChromeOS build.
350
351 Args:
352 spec: a string build spec for RO or RW firmware, eg. firmware,
353 cros. For RO firmware, the value can also be released_ro_X.
354 board: a string board against which this task will run suite job.
Xinan Lin028f9582019-12-11 10:55:33 -0800355 firmware_build_dict: a dict of firmware artifacts, see return value of
356 |base_event.get_firmware_builds|.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700357 lab_config: a config.LabConfig object, to read lab config file.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700358
359 Returns:
360 A string firmware build name.
361
362 Raises:
363 ValueError: if failing to get firmware from lab config file;
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700364 """
365 if not spec or spec == 'stable':
366 # TODO(crbug.com/577316): Query stable RO firmware.
367 logging.debug('%s RO firmware build is not supported.', spec)
368 return None
369
370 try:
371 if spec.startswith(_RELEASE_RO_FIRMWARE_SPEC):
372 # For RO firmware whose value is released_ro_X, where X is the index of
373 # the list defined in lab config file:
374 # CROS/RELEASED_RO_BUILDS_[board].
375 # For example, for spec 'released_ro_2', and lab config file
376 # CROS/RELEASED_RO_BUILDS_veyron_jerry: build1,build2,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600377 # return firmware RO build should be 'build2'.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700378 return self._get_latest_firmware_build_from_lab_config(
379 spec, board, lab_config)
Xinan Lin028f9582019-12-11 10:55:33 -0800380 elif firmware_build_dict:
381 return firmware_build_dict.get((spec, board), None)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700382 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800383 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700384 except ValueError as e:
385 logging.warning('Failed to get firmware from lab config file'
386 'for spec %s, board %s: %s', spec, board, str(e))
387 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700388
C Shapiro7f24a002017-12-05 14:25:09 -0700389 def _push_suite(
390 self,
Xinan Lindf0698a2020-02-05 22:38:11 -0800391 task_id=None,
C Shapiro7f24a002017-12-05 14:25:09 -0700392 board=None,
393 model=None,
394 cros_build=None,
395 firmware_rw_build=None,
396 firmware_ro_build=None,
397 test_source_build=None,
398 launch_control_build=None,
Xinan Lin4757d6f2020-03-24 22:20:31 -0700399 run_prod_code=False):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700400 """Schedule suite job for the task by pushing suites to SuiteQueue.
401
402 Args:
Xinan Lindf0698a2020-02-05 22:38:11 -0800403 task_id: the id to track this task in exectuion.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700404 board: the board against which this suite job run.
C Shapiro7f24a002017-12-05 14:25:09 -0700405 model: the model name for unibuild.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700406 cros_build: the CrOS build of this suite job.
407 firmware_rw_build: Firmware RW build to run this suite job with.
408 firmware_ro_build: Firmware RO build to run this suite job with.
409 test_source_build: Test source build, used for server-side
410 packaging of this suite job.
411 launch_control_build: the launch control build of this suite job.
412 run_prod_code: If True, the suite will run the test code that lives
413 in prod aka the test code currently on the lab servers. If
414 False, the control files and test code for this suite run will
415 be retrieved from the build artifacts. Default is False.
416 """
417 android_build = None
418 testbed_build = None
419
420 if self.testbed_dut_count:
421 launch_control_build = '%s#%d' % (launch_control_build,
422 self.testbed_dut_count)
423 test_source_build = launch_control_build
424 board = '%s-%d' % (board, self.testbed_dut_count)
425
426 if launch_control_build:
427 if not self.testbed_dut_count:
428 android_build = launch_control_build
429 else:
430 testbed_build = launch_control_build
431
432 suite_job_parameters = {
433 'suite': self.suite,
Xinan Lindf0698a2020-02-05 22:38:11 -0800434 'task_id': task_id,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700435 'board': board,
C Shapiro7f24a002017-12-05 14:25:09 -0700436 'model': model,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700437 build_lib.BuildVersionKey.CROS_VERSION: cros_build,
438 build_lib.BuildVersionKey.FW_RW_VERSION: firmware_rw_build,
439 build_lib.BuildVersionKey.FW_RO_VERSION: firmware_ro_build,
440 build_lib.BuildVersionKey.ANDROID_BUILD_VERSION: android_build,
441 build_lib.BuildVersionKey.TESTBED_BUILD_VERSION: testbed_build,
442 'num': self.num,
443 'pool': self.pool,
444 'priority': self.priority,
445 'timeout': self.timeout,
446 'timeout_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
447 'max_runtime_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
Xixuan Wu2ba72652017-09-15 15:49:42 -0700448 'no_wait_for_results': not self.job_retry,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700449 'test_source_build': test_source_build,
450 'job_retry': self.job_retry,
451 'no_delay': self.no_delay,
Xixuan Wu80531932017-10-12 17:26:51 -0700452 'force': self.force,
Xinan Linc8647112020-02-04 16:45:56 -0800453 'dimensions': self.dimensions,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700454 'run_prod_code': run_prod_code,
Xinan Lin4757d6f2020-03-24 22:20:31 -0700455 'qs_account': self.qs_account,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700456 }
457
Xinan Lin9e4917d2019-11-04 10:58:47 -0800458 task_executor.push(task_executor.SUITES_QUEUE,
459 tag=self.suite,
460 **suite_job_parameters)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700461 logging.info('Pushing task %r into taskqueue', suite_job_parameters)
Xixuan Wu5451a662017-10-17 10:57:40 -0700462 self.is_pushed = True
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700463
Xinan Lin028f9582019-12-11 10:55:33 -0800464 def _schedule_cros_builds(self, build_dict, firmware_build_dict, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700465 """Schedule tasks with branch builds.
466
467 Args:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600468 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800469 value of |build_lib.get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800470 firmware_build_dict: a dict of firmware artifact, see return value of
471 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700472 configs: A config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700473 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700474 lab_config = configs.lab_config
C Shapiro7f24a002017-12-05 14:25:09 -0700475 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
C Shapiro09108252019-08-01 14:52:52 -0500476 model_agnostic_cros_builds = set()
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700477 for (board, passed_model, build_type,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600478 milestone), manifest in build_dict.iteritems():
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700479 cros_build = str(build_lib.CrOSBuild(board, build_type, milestone,
480 manifest))
481 logging.info('Running %s on %s', self.name, cros_build)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700482 if self.exclude_boards and board in self.exclude_boards:
483 logging.debug('Board %s is in excluded board list: %s',
484 board, self.exclude_boards)
485 continue
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700486
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700487 if self.boards and board not in self.boards:
488 logging.debug('Board %s is not in supported board list: %s',
489 board, self.boards)
490 continue
491
492 # Check the fitness of the build's branch for task
493 branch_build_spec = _pick_branch(build_type, milestone)
494 if not self._fits_spec(branch_build_spec):
Xinan Lindf0698a2020-02-05 22:38:11 -0800495 msg = ("branch_build spec %s doesn't fit this task's "
496 "requirement: %s") % (branch_build_spec,
497 ",".join(self.branch_specs))
498 logging.debug(msg)
499 self.job_section.add_schedule_job(board, passed_model, msg=msg)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700500 continue
501
Xinan Lindf0698a2020-02-05 22:38:11 -0800502 # Record this build as it matches both board and branch specs.
503 if self.only_hwtest_sanity_required:
504 self.job_section.add_matched_relax_build(
505 board, build_type, milestone, manifest)
506 else:
507 self.job_section.add_matched_build(
508 board, build_type, milestone, manifest)
509
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700510 firmware_rw_build = None
511 firmware_ro_build = None
512 if self.firmware_rw_build_spec or self.firmware_ro_build_spec:
513 firmware_rw_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800514 self.firmware_rw_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700515 firmware_ro_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800516 self.firmware_ro_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700517
518 if not firmware_ro_build and self.firmware_ro_build_spec:
Xinan Lindf0698a2020-02-05 22:38:11 -0800519 msg = 'No RO firmware ro build to run, skip running'
520 logging.debug(msg)
521 self.job_section.add_schedule_job(board, passed_model, msg=msg)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700522 continue
523
524 if self.test_source == build_lib.BuildType.FIRMWARE_RW:
525 test_source_build = firmware_rw_build
526 else:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700527 # Default test source build to CrOS build if it's not specified.
528 # Past versions chose based on run_prod_code, but we no longer respect
529 # that option and scheduler settings should always set it to False.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700530 test_source_build = cros_build
531
Xinan Lindf0698a2020-02-05 22:38:11 -0800532 # Record the matched firwmare build.
533 if firmware_rw_build:
534 self.job_section.add_matched_fw_build(
535 board,
536 self.firmware_rw_build_spec,
537 firmware_rw_build,
538 read_only=False)
539 if firmware_ro_build:
540 self.job_section.add_matched_fw_build(
541 board,
542 self.firmware_ro_build_spec,
543 firmware_ro_build,
544 read_only=True)
545
Xixuan Wub4b2f412019-05-03 11:22:31 -0700546 hwtest_board = build_lib.reshape_board(board)
Xinan Lin0550f492020-01-21 16:25:53 -0800547
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000548 models = models_by_board.get(board, [None])
C Shapiro7f24a002017-12-05 14:25:09 -0700549
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000550 for model in models:
551 if ((passed_model is not None and model == passed_model) or
552 passed_model is None):
Xixuan Wua41efa22019-05-17 14:28:04 -0700553 full_model_name = '%s_%s' % (board, model)
554 # Respect exclude first.
555 if self.exclude_models and full_model_name in self.exclude_models:
556 logging.debug("Skip model %s as it's in exclude model list %s",
557 model, self.exclude_models)
558 continue
559
560 if self.models and full_model_name not in self.models:
561 logging.debug("Skip model %s as it's not in support model list %s",
562 model, self.models)
563 continue
564
C Shapiro09108252019-08-01 14:52:52 -0500565 explicit_model = model
566
567 if self.any_model:
Xinan Lin3ba18a02019-08-13 15:44:55 -0700568 explicit_model = None
C Shapiro09108252019-08-01 14:52:52 -0500569 unique_build = str(cros_build)
570 if unique_build in model_agnostic_cros_builds:
571 # Skip since we've already run with no explicit model set.
Xinan Lindf0698a2020-02-05 22:38:11 -0800572 msg = "Skip model %s as any_model enabled for this job." % model
573 self.job_section.add_schedule_job(board, model, msg=msg)
C Shapiro09108252019-08-01 14:52:52 -0500574 continue
575 model_agnostic_cros_builds.add(unique_build)
576
Xinan Lindf0698a2020-02-05 22:38:11 -0800577 task_id = str(uuid.uuid1())
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000578 self._push_suite(
Xinan Lindf0698a2020-02-05 22:38:11 -0800579 task_id=task_id,
Xixuan Wub4b2f412019-05-03 11:22:31 -0700580 board=hwtest_board,
C Shapiro09108252019-08-01 14:52:52 -0500581 model=explicit_model,
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000582 cros_build=cros_build,
583 firmware_rw_build=firmware_rw_build,
584 firmware_ro_build=firmware_ro_build,
Xinan Lin0550f492020-01-21 16:25:53 -0800585 test_source_build=test_source_build)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700586
Xinan Lindf0698a2020-02-05 22:38:11 -0800587 self.job_section.add_schedule_job(board, model, task_id=task_id)
588
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700589 def _schedule_launch_control_builds(self, launch_control_builds):
590 """Schedule tasks with launch control builds.
591
592 Args:
593 launch_control_builds: the build dict for Android boards.
594 """
595 for board, launch_control_build in launch_control_builds.iteritems():
596 logging.debug('Running %s on %s', self.name, board)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700597 if self.exclude_boards and board in self.exclude_boards:
598 logging.debug('Board %s is in excluded board list: %s',
599 board, self.exclude_boards)
600 continue
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700601 if self.boards and board not in self.boards:
602 logging.debug('Board %s is not in supported board list: %s',
603 board, self.boards)
604 continue
605
606 for android_build in launch_control_build:
607 if not any([branch in android_build
608 for branch in self.launch_control_branches]):
609 logging.debug('Branch %s is not required to run for task '
610 '%s', android_build, self.name)
611 continue
612
613 self._push_suite(board=board,
614 test_source_build=android_build,
615 launch_control_build=android_build)
616
617
618def _pick_branch(build_type, milestone):
619 """Select branch based on build type.
620
621 If the build_type is a bare branch, return build_type as the build spec.
622 If the build_type is a normal CrOS branch, return milestone as the build
623 spec.
624
625 Args:
626 build_type: a string builder name, like 'release'.
627 milestone: a string milestone, like '55'.
628
629 Returns:
630 A string milestone if build_type represents CrOS build, otherwise
631 return build_type.
632 """
633 return build_type if build_type in build_lib.BARE_BRANCHES else milestone
Xinan Linae7d6372019-09-12 14:42:10 -0700634
635
636def _split_unibuilds(build_dict, configs):
637 """Split the uni-builds to all models under a board.
638
639 Args:
640 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800641 value of |build_lib.get_cros_builds|.
Xinan Linae7d6372019-09-12 14:42:10 -0700642 configs: a config_reader.Configs object.
643
644 Returns:
645 A build dict.
646 """
647 models_by_board = configs.lab_config.get_cros_model_map()
648 if not models_by_board:
649 return build_dict
650 all_branch_build_dict = {}
651 for (board, model, config, milestone), platform in build_dict.iteritems():
652 uni_build_models = models_by_board.get(board)
653 if uni_build_models is not None and model is None:
654 for uni_build_model in uni_build_models:
655 model_key = (board, uni_build_model, config, milestone)
656 _add_build_dict(all_branch_build_dict, model_key, platform)
657 continue
658 build_key = (board, model, config, milestone)
659 _add_build_dict(all_branch_build_dict, build_key, platform)
660
661 return all_branch_build_dict
662
663
664def _add_build_dict(build_dict, key, value):
665 """A wrapper to add or update an item in build_dict."""
666 cur_manifest = build_dict.get(key)
667 if cur_manifest is None:
668 build_dict[key] = value
669 return
670 build_dict[key] = max(
671 [cur_manifest, value], key=version.LooseVersion)