blob: 713c1fe73c319e9880b0850545e55382c5d520f7 [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
Brigit Rossbachd55ac532020-09-10 13:37:21 +000014import constants
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070015import task_executor
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070016import tot_manager
17
18# The max lifetime of a suite scheduled by suite scheduler
Brigit Rossbachd55ac532020-09-10 13:37:21 +000019_JOB_MAX_RUNTIME_MINS_DEFAULT = constants.Buildbucket.MAX_BUILDBUCKET_TIMEOUT_MINS
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070020
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070021
22class SchedulingError(Exception):
23 """Raised to indicate a failure in scheduling a task."""
24
25
26class Task(object):
27 """Represents an entry from the suite_scheduler config file.
28
29 Each entry from the suite_scheduler config file maps one-to-one to a
30 Task. Each instance has enough information to schedule itself.
31 """
32
Xinan Lin33937d62020-04-14 14:41:23 -070033 def __init__(self,
34 task_info,
35 board_family_config={},
36 tot=None,
37 is_sanity=False):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070038 """Initialize a task instance.
39
40 Args:
41 task_info: a config_reader.TaskInfo object, which includes:
42 name, name of this task, e.g. 'NightlyPower'
43 suite, the name of the suite to run, e.g. 'graphics_per-day'
44 branch_specs, a pre-vetted iterable of branch specifiers,
45 e.g. ['>=R18', 'factory']
46 pool, the pool of machines to schedule tasks. Default is None.
47 num, the number of devices to shard the test suite. It could
48 be an Integer or None. By default it's None.
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.
81 test_source, the source of test code when firmware will be updated in
82 the test. The value can be 'firmware_rw', 'firmware_ro' or 'cros'.
83 job_retry, set to True to enable job-level retry. Default is False.
Xixuan Wu80531932017-10-12 17:26:51 -070084 no_delay, set to True to raise the priority of this task in task.
85 force, set to True to schedule this suite no matter whether there's
86 duplicate jobs before.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -070087 queue, so the suite jobs can start running tests with no waiting.
88 hour, an integer specifying the hour that a nightly run should be
89 triggered, default is set to 21.
90 day, an integer specifying the day of a week that a weekly run should
91 be triggered, default is set to 5 (Saturday).
92 os_type, type of OS, e.g., cros, brillo, android. Default is cros.
93 The argument is required for android/brillo builds.
94 launch_control_branches, comma separated string of launch control
95 branches. The argument is required and only applicable for
96 android/brillo builds.
97 launch_control_targets, comma separated string of build targets for
98 launch control builds. The argument is required and only
99 applicable for android/brillo builds.
100 testbed_dut_count, number of duts to test when using a testbed.
Xinan Lin4757d6f2020-03-24 22:20:31 -0700101 qs_account, quota account for the unmanaged pool which has enabled
102 Quota Scheduler.
Jacob Kopczynskif464c8f2020-08-20 17:54:35 +0000103 migrations, list of migration flags (strings) to set. Must name
104 boolean fields, which will be set to True.
Jacob Kopczynskic54520f2020-08-07 13:20:12 -0700105 pubsub_topic, Long-form topic string, will receive Swarming's native
106 pubsub messages. Format is "projects/<proj>/topics/<topic>"
107
Xixuan Wu83118dd2018-08-27 12:11:35 -0700108 board_family_config: A board family dictionary mapping board_family name
109 to its corresponding boards.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700110 tot: The tot manager for checking ToT. If it's None, a new tot_manager
111 instance will be initialized.
Xinan Lin33937d62020-04-14 14:41:23 -0700112 is_sanity: A boolean; true if we are running in sanity env.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700113 """
Xixuan Wu5451a662017-10-17 10:57:40 -0700114 # Indicate whether there're suites get pushed into taskqueue for this task.
115 self.is_pushed = False
116
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700117 self.branch_specs = task_info.branch_specs
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700118 self.cros_build_spec = task_info.cros_build_spec
Xixuan Wu008ee832017-10-12 16:59:34 -0700119 self.day = task_info.day
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700120 self.firmware_ro_build_spec = task_info.firmware_ro_build_spec
121 self.firmware_rw_build_spec = task_info.firmware_rw_build_spec
122 self.force = task_info.force
123 self.frontdoor = task_info.frontdoor
124 self.hour = task_info.hour
125 self.job_retry = task_info.job_retry
Jacob Kopczynskic54520f2020-08-07 13:20:12 -0700126 self.migrations = task_info.migrations
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700127 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
Jacob Kopczynskic54520f2020-08-07 13:20:12 -0700134 self.pubsub_topic = task_info.pubsub_topic
Xinan Lin4757d6f2020-03-24 22:20:31 -0700135 self.qs_account = task_info.qs_account
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700136 self.suite = task_info.suite
137 self.test_source = task_info.test_source
138 self.testbed_dut_count = task_info.testbed_dut_count
139 self.timeout = task_info.timeout
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700140
141 if task_info.lc_branches:
142 self.launch_control_branches = [
143 t.strip() for t in task_info.lc_branches.split(',')]
144 else:
145 self.launch_control_branches = []
146
147 if task_info.lc_targets:
148 self.launch_control_targets = [
149 t.strip() for t in task_info.lc_targets.split(',')]
150 else:
151 self.launch_control_targets = []
152
153 if task_info.boards:
154 self.boards = [t.strip() for t in task_info.boards.split(',')]
155 else:
156 self.boards = []
157
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700158 if task_info.exclude_boards:
159 self.exclude_boards = [
160 t.strip() for t in task_info.exclude_boards.split(',')]
161 else:
162 self.exclude_boards = []
163
Xixuan Wu89897182019-01-03 15:28:01 -0800164 if task_info.models:
165 self.models = [t.strip() for t in task_info.models.split(',')]
166 else:
167 self.models = []
168
169 if task_info.exclude_models:
170 self.exclude_models = [
171 t.strip() for t in task_info.exclude_models.split(',')]
172 else:
173 self.exclude_models = []
174
C Shapiro09108252019-08-01 14:52:52 -0500175 self.any_model = task_info.any_model
176
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700177 if task_info.board_families:
178 # Finetune the allowed boards list with board_families & boards.
179 families = [family.strip()
180 for family in task_info.board_families.split(',')]
181 for family in families:
182 self.boards += board_family_config.get(family, [])
Xixuan Wu89897182019-01-03 15:28:01 -0800183
Po-Hsien Wangdd833072018-08-16 18:09:20 -0700184 if task_info.exclude_board_families:
185 # Finetune the disallowed boards list with exclude_board_families
186 # & exclude_boards.
187 families = [family.strip()
188 for family in task_info.exclude_board_families.split(',')]
189 for family in families:
190 self.exclude_boards += board_family_config.get(family, [])
191
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700192 if tot is None:
193 self.tot_manager = tot_manager.TotMilestoneManager()
194 else:
195 self.tot_manager = tot
196
Xinan Linc8647112020-02-04 16:45:56 -0800197 self.dimensions = task_info.dimensions
198
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700199 self._set_spec_compare_info()
Xinan Lin33937d62020-04-14 14:41:23 -0700200 # Sanity test does not have to upload metrics.
201 if not is_sanity:
202 self.job_section = analytics.ScheduleJobSection(task_info)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700203
Xinan Lin028f9582019-12-11 10:55:33 -0800204 def schedule(self, launch_control_builds, cros_builds_tuple,
205 firmware_builds, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700206 """Schedule the task by its settings.
207
208 Args:
209 launch_control_builds: the build dict for Android boards, see
210 return value of |get_launch_control_builds|.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600211 cros_builds_tuple: the two-tuple of build dicts for ChromeOS boards,
212 see return value of |get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800213 firmware_builds: a dict of firmware artifact, see return value of
214 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700215 configs: a config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700216
217 Raises:
218 SchedulingError: if tasks that should be scheduled fail to schedule.
Xixuan Wu5451a662017-10-17 10:57:40 -0700219
220 Returns:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700221 A boolean indicator; true if there were any suites related to this
222 task which got pushed into the suites queue.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700223 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700224 assert configs.lab_config is not None
Xixuan Wu5451a662017-10-17 10:57:40 -0700225 self.is_pushed = False
226
Craig Bergstrom58263d32018-04-26 14:11:35 -0600227 branch_builds, relaxed_builds = cros_builds_tuple
228 builds_dict = branch_builds
229 if self.only_hwtest_sanity_required:
Xinan Linae7d6372019-09-12 14:42:10 -0700230 builds_dict = _split_unibuilds(relaxed_builds, configs)
Craig Bergstrom58263d32018-04-26 14:11:35 -0600231
Xinan Lindf0698a2020-02-05 22:38:11 -0800232 # Record all target boards and models into job section.
233 lab_config = configs.lab_config
234 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
235 boards = self.boards if self.boards else lab_config.get_cros_board_list()
236 for b in boards:
237 if self.exclude_boards and b in self.exclude_boards:
238 continue
239 self.job_section.add_board(b)
240 models = self.models or models_by_board.get(b, [])
241 for m in models:
242 if m and '%s_%s' % (b, m) not in self.exclude_models:
243 self.job_section.add_model(m)
244
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700245 logging.info('######## Scheduling task %s ########', self.name)
246 if self.os_type == build_lib.OS_TYPE_CROS:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600247 if not builds_dict:
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700248 logging.info('No CrOS build to run, skip running.')
249 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800250 self._schedule_cros_builds(builds_dict, firmware_builds, configs)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700251 else:
252 if not launch_control_builds:
253 logging.info('No Android build to run, skip running.')
254 else:
255 self._schedule_launch_control_builds(launch_control_builds)
Xinan Lindf0698a2020-02-05 22:38:11 -0800256 upload_result = False
257 try:
258 upload_result = self.job_section.upload()
259 # For any exceptions from BQ, only log it and move on.
260 except Exception as e: #pylint: disable=broad-except
261 logging.exception(str(e))
262 if not upload_result:
263 logging.warning('Failed to insert row: %r', self.job_section)
Xixuan Wu5451a662017-10-17 10:57:40 -0700264 return self.is_pushed
265
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700266 def _set_spec_compare_info(self):
267 """Set branch spec compare info for task for further check."""
268 self._bare_branches = []
269 self._version_equal_constraint = False
270 self._version_gte_constraint = False
271 self._version_lte_constraint = False
272
273 if not self.branch_specs:
274 # Any milestone is OK.
275 self._numeric_constraint = version.LooseVersion('0')
276 else:
277 self._numeric_constraint = None
278 for spec in self.branch_specs:
279 if 'tot' in spec.lower():
280 # Convert spec >=tot-1 to >=RXX.
281 tot_str = spec[spec.index('tot'):]
282 spec = spec.replace(
283 tot_str, self.tot_manager.convert_tot_spec(tot_str))
284
285 if spec.startswith('>='):
286 self._numeric_constraint = version.LooseVersion(
287 spec.lstrip('>=R'))
288 self._version_gte_constraint = True
289 elif spec.startswith('<='):
290 self._numeric_constraint = version.LooseVersion(
291 spec.lstrip('<=R'))
292 self._version_lte_constraint = True
293 elif spec.startswith('=='):
294 self._version_equal_constraint = True
295 self._numeric_constraint = version.LooseVersion(
296 spec.lstrip('==R'))
297 else:
298 self._bare_branches.append(spec)
299
300 def _fits_spec(self, branch):
301 """Check if a branch is deemed OK by this task's branch specs.
302
303 Will return whether a branch 'fits' the specifications stored in this task.
304
305 Examples:
306 Assuming tot=R40
307 t = Task('Name', 'suite', ['factory', '>=tot-1'])
308 t._fits_spec('factory') # True
309 t._fits_spec('40') # True
310 t._fits_spec('38') # False
311 t._fits_spec('firmware') # False
312
313 Args:
314 branch: the branch to check.
315
316 Returns:
317 True if branch 'fits' with stored specs, False otherwise.
318 """
319 if branch in build_lib.BARE_BRANCHES:
320 return branch in self._bare_branches
321
322 if self._numeric_constraint:
323 if self._version_equal_constraint:
324 return version.LooseVersion(branch) == self._numeric_constraint
325 elif self._version_gte_constraint:
326 return version.LooseVersion(branch) >= self._numeric_constraint
327 elif self._version_lte_constraint:
328 return version.LooseVersion(branch) <= self._numeric_constraint
329 else:
330 return version.LooseVersion(branch) >= self._numeric_constraint
331 else:
332 return False
333
Xinan Lin028f9582019-12-11 10:55:33 -0800334 def _get_firmware_build(self, spec, board, firmware_build_dict, lab_config):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700335 """Get the firmware build name to test with ChromeOS build.
336
337 Args:
338 spec: a string build spec for RO or RW firmware, eg. firmware,
339 cros. For RO firmware, the value can also be released_ro_X.
340 board: a string board against which this task will run suite job.
Xinan Lin028f9582019-12-11 10:55:33 -0800341 firmware_build_dict: a dict of firmware artifacts, see return value of
342 |base_event.get_firmware_builds|.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700343 lab_config: a config.LabConfig object, to read lab config file.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700344
345 Returns:
346 A string firmware build name.
347
348 Raises:
349 ValueError: if failing to get firmware from lab config file;
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700350 """
351 if not spec or spec == 'stable':
352 # TODO(crbug.com/577316): Query stable RO firmware.
353 logging.debug('%s RO firmware build is not supported.', spec)
354 return None
355
356 try:
Dhanya Ganeshf6014b72020-08-04 22:33:28 +0000357 if firmware_build_dict:
Xinan Lin028f9582019-12-11 10:55:33 -0800358 return firmware_build_dict.get((spec, board), None)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700359 else:
Xinan Lin028f9582019-12-11 10:55:33 -0800360 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700361 except ValueError as e:
362 logging.warning('Failed to get firmware from lab config file'
363 'for spec %s, board %s: %s', spec, board, str(e))
364 return None
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700365
C Shapiro7f24a002017-12-05 14:25:09 -0700366 def _push_suite(
367 self,
Xinan Lindf0698a2020-02-05 22:38:11 -0800368 task_id=None,
C Shapiro7f24a002017-12-05 14:25:09 -0700369 board=None,
370 model=None,
371 cros_build=None,
372 firmware_rw_build=None,
373 firmware_ro_build=None,
374 test_source_build=None,
375 launch_control_build=None,
Xinan Lin4757d6f2020-03-24 22:20:31 -0700376 run_prod_code=False):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700377 """Schedule suite job for the task by pushing suites to SuiteQueue.
378
379 Args:
Xinan Lindf0698a2020-02-05 22:38:11 -0800380 task_id: the id to track this task in exectuion.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700381 board: the board against which this suite job run.
C Shapiro7f24a002017-12-05 14:25:09 -0700382 model: the model name for unibuild.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700383 cros_build: the CrOS build of this suite job.
384 firmware_rw_build: Firmware RW build to run this suite job with.
385 firmware_ro_build: Firmware RO build to run this suite job with.
386 test_source_build: Test source build, used for server-side
387 packaging of this suite job.
388 launch_control_build: the launch control build of this suite job.
389 run_prod_code: If True, the suite will run the test code that lives
390 in prod aka the test code currently on the lab servers. If
391 False, the control files and test code for this suite run will
392 be retrieved from the build artifacts. Default is False.
393 """
394 android_build = None
395 testbed_build = None
396
397 if self.testbed_dut_count:
398 launch_control_build = '%s#%d' % (launch_control_build,
399 self.testbed_dut_count)
400 test_source_build = launch_control_build
401 board = '%s-%d' % (board, self.testbed_dut_count)
402
403 if launch_control_build:
404 if not self.testbed_dut_count:
405 android_build = launch_control_build
406 else:
407 testbed_build = launch_control_build
408
409 suite_job_parameters = {
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700410 build_lib.BuildVersionKey.ANDROID_BUILD_VERSION: android_build,
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700411 build_lib.BuildVersionKey.CROS_VERSION: cros_build,
412 build_lib.BuildVersionKey.FW_RO_VERSION: firmware_ro_build,
413 build_lib.BuildVersionKey.FW_RW_VERSION: firmware_rw_build,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700414 build_lib.BuildVersionKey.TESTBED_BUILD_VERSION: testbed_build,
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,
Jacob Kopczynskie56bf492020-08-07 13:20:12 -0700420 'migrations': self.migrations,
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700421 'model': model,
422 'name': self.name,
423 'no_delay': self.no_delay,
424 'no_wait_for_results': not self.job_retry,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700425 'num': self.num,
426 'pool': self.pool,
427 'priority': self.priority,
Jacob Kopczynskie56bf492020-08-07 13:20:12 -0700428 'pubsub_topic': self.pubsub_topic,
Jacob Kopczynskic1d0f5c2020-08-09 10:41:32 -0700429 'qs_account': self.qs_account,
430 'run_prod_code': run_prod_code,
431 'suite': self.suite,
432 'task_id': task_id,
433 'test_source_build': test_source_build,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700434 'timeout': self.timeout,
435 'timeout_mins': _JOB_MAX_RUNTIME_MINS_DEFAULT,
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700436 }
437
Xinan Lin9e4917d2019-11-04 10:58:47 -0800438 task_executor.push(task_executor.SUITES_QUEUE,
439 tag=self.suite,
440 **suite_job_parameters)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700441 logging.info('Pushing task %r into taskqueue', suite_job_parameters)
Xixuan Wu5451a662017-10-17 10:57:40 -0700442 self.is_pushed = True
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700443
Xinan Lin028f9582019-12-11 10:55:33 -0800444 def _schedule_cros_builds(self, build_dict, firmware_build_dict, configs):
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700445 """Schedule tasks with branch builds.
446
447 Args:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600448 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800449 value of |build_lib.get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800450 firmware_build_dict: a dict of firmware artifact, see return value of
451 |base_event.get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700452 configs: A config_reader.Configs object.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700453 """
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700454 lab_config = configs.lab_config
C Shapiro7f24a002017-12-05 14:25:09 -0700455 models_by_board = lab_config.get_cros_model_map() if lab_config else {}
C Shapiro09108252019-08-01 14:52:52 -0500456 model_agnostic_cros_builds = set()
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700457 for (board, passed_model, build_type,
Craig Bergstrom58263d32018-04-26 14:11:35 -0600458 milestone), manifest in build_dict.iteritems():
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700459 cros_build = str(build_lib.CrOSBuild(board, build_type, milestone,
460 manifest))
461 logging.info('Running %s on %s', self.name, cros_build)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700462 if self.exclude_boards and board in self.exclude_boards:
463 logging.debug('Board %s is in excluded board list: %s',
464 board, self.exclude_boards)
465 continue
Xixuan Wu8d2f2862018-08-28 16:48:04 -0700466
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700467 if self.boards and board not in self.boards:
468 logging.debug('Board %s is not in supported board list: %s',
469 board, self.boards)
470 continue
471
472 # Check the fitness of the build's branch for task
473 branch_build_spec = _pick_branch(build_type, milestone)
474 if not self._fits_spec(branch_build_spec):
Xinan Lindf0698a2020-02-05 22:38:11 -0800475 msg = ("branch_build spec %s doesn't fit this task's "
476 "requirement: %s") % (branch_build_spec,
477 ",".join(self.branch_specs))
478 logging.debug(msg)
479 self.job_section.add_schedule_job(board, passed_model, msg=msg)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700480 continue
481
Xinan Lindf0698a2020-02-05 22:38:11 -0800482 # Record this build as it matches both board and branch specs.
483 if self.only_hwtest_sanity_required:
484 self.job_section.add_matched_relax_build(
485 board, build_type, milestone, manifest)
486 else:
487 self.job_section.add_matched_build(
488 board, build_type, milestone, manifest)
489
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700490 firmware_rw_build = None
491 firmware_ro_build = None
492 if self.firmware_rw_build_spec or self.firmware_ro_build_spec:
493 firmware_rw_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800494 self.firmware_rw_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700495 firmware_ro_build = self._get_firmware_build(
Xinan Lin028f9582019-12-11 10:55:33 -0800496 self.firmware_ro_build_spec, board, firmware_build_dict, lab_config)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700497
498 if not firmware_ro_build and self.firmware_ro_build_spec:
Xinan Lindf0698a2020-02-05 22:38:11 -0800499 msg = 'No RO firmware ro build to run, skip running'
500 logging.debug(msg)
501 self.job_section.add_schedule_job(board, passed_model, msg=msg)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700502 continue
503
504 if self.test_source == build_lib.BuildType.FIRMWARE_RW:
505 test_source_build = firmware_rw_build
506 else:
Jacob Kopczynski79d00102018-07-13 15:37:03 -0700507 # Default test source build to CrOS build if it's not specified.
508 # Past versions chose based on run_prod_code, but we no longer respect
509 # that option and scheduler settings should always set it to False.
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700510 test_source_build = cros_build
511
Xinan Lindf0698a2020-02-05 22:38:11 -0800512 # Record the matched firwmare build.
513 if firmware_rw_build:
514 self.job_section.add_matched_fw_build(
515 board,
516 self.firmware_rw_build_spec,
517 firmware_rw_build,
518 read_only=False)
519 if firmware_ro_build:
520 self.job_section.add_matched_fw_build(
521 board,
522 self.firmware_ro_build_spec,
523 firmware_ro_build,
524 read_only=True)
525
Xinan Lin6668e0f2020-05-29 10:02:57 -0700526 # Board above is used as build target to control the CrOS image.
527 # The following part is to assign models for lab boards, where
528 # the suffix should be removed.
Prathmesh Prabhu074b8d42020-08-13 17:17:33 +0000529 hwtest_board = build_lib.reshape_board(board)
530
Xinan Lin6668e0f2020-05-29 10:02:57 -0700531 models = models_by_board.get(hwtest_board, [None])
C Shapiro7f24a002017-12-05 14:25:09 -0700532
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000533 for model in models:
534 if ((passed_model is not None and model == passed_model) or
535 passed_model is None):
Xinan Lin6668e0f2020-05-29 10:02:57 -0700536 full_model_name = '%s_%s' % (hwtest_board, model)
Xixuan Wua41efa22019-05-17 14:28:04 -0700537 # Respect exclude first.
538 if self.exclude_models and full_model_name in self.exclude_models:
539 logging.debug("Skip model %s as it's in exclude model list %s",
540 model, self.exclude_models)
541 continue
542
543 if self.models and full_model_name not in self.models:
544 logging.debug("Skip model %s as it's not in support model list %s",
545 model, self.models)
546 continue
547
C Shapiro09108252019-08-01 14:52:52 -0500548 explicit_model = model
549
550 if self.any_model:
Xinan Lin3ba18a02019-08-13 15:44:55 -0700551 explicit_model = None
C Shapiro09108252019-08-01 14:52:52 -0500552 unique_build = str(cros_build)
553 if unique_build in model_agnostic_cros_builds:
554 # Skip since we've already run with no explicit model set.
Xinan Lindf0698a2020-02-05 22:38:11 -0800555 msg = "Skip model %s as any_model enabled for this job." % model
556 self.job_section.add_schedule_job(board, model, msg=msg)
C Shapiro09108252019-08-01 14:52:52 -0500557 continue
558 model_agnostic_cros_builds.add(unique_build)
559
Xinan Lindf0698a2020-02-05 22:38:11 -0800560 task_id = str(uuid.uuid1())
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000561 self._push_suite(
Xinan Lindf0698a2020-02-05 22:38:11 -0800562 task_id=task_id,
Xixuan Wub4b2f412019-05-03 11:22:31 -0700563 board=hwtest_board,
C Shapiro09108252019-08-01 14:52:52 -0500564 model=explicit_model,
Harpreet Grewalbbbb7de2019-02-05 19:35:03 +0000565 cros_build=cros_build,
566 firmware_rw_build=firmware_rw_build,
567 firmware_ro_build=firmware_ro_build,
Xinan Lin0550f492020-01-21 16:25:53 -0800568 test_source_build=test_source_build)
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700569
Xinan Lin6668e0f2020-05-29 10:02:57 -0700570 # Analytics table stores the build target instead of the lab board.
Xinan Lin0673a182020-04-14 15:09:35 -0700571 self.job_section.add_schedule_job(
572 board, explicit_model, task_id=task_id)
Xinan Lindf0698a2020-02-05 22:38:11 -0800573
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700574 def _schedule_launch_control_builds(self, launch_control_builds):
575 """Schedule tasks with launch control builds.
576
577 Args:
578 launch_control_builds: the build dict for Android boards.
579 """
580 for board, launch_control_build in launch_control_builds.iteritems():
581 logging.debug('Running %s on %s', self.name, board)
Po-Hsien Wang6d589732018-05-15 17:19:34 -0700582 if self.exclude_boards and board in self.exclude_boards:
583 logging.debug('Board %s is in excluded board list: %s',
584 board, self.exclude_boards)
585 continue
Xixuan Wu0c76d5b2017-08-30 16:40:17 -0700586 if self.boards and board not in self.boards:
587 logging.debug('Board %s is not in supported board list: %s',
588 board, self.boards)
589 continue
590
591 for android_build in launch_control_build:
592 if not any([branch in android_build
593 for branch in self.launch_control_branches]):
594 logging.debug('Branch %s is not required to run for task '
595 '%s', android_build, self.name)
596 continue
597
598 self._push_suite(board=board,
599 test_source_build=android_build,
600 launch_control_build=android_build)
601
602
603def _pick_branch(build_type, milestone):
604 """Select branch based on build type.
605
606 If the build_type is a bare branch, return build_type as the build spec.
607 If the build_type is a normal CrOS branch, return milestone as the build
608 spec.
609
610 Args:
611 build_type: a string builder name, like 'release'.
612 milestone: a string milestone, like '55'.
613
614 Returns:
615 A string milestone if build_type represents CrOS build, otherwise
616 return build_type.
617 """
618 return build_type if build_type in build_lib.BARE_BRANCHES else milestone
Xinan Linae7d6372019-09-12 14:42:10 -0700619
620
621def _split_unibuilds(build_dict, configs):
622 """Split the uni-builds to all models under a board.
623
624 Args:
625 build_dict: the build dict for ChromeOS boards, see return
Xinan Linea1efcb2019-12-30 23:46:42 -0800626 value of |build_lib.get_cros_builds|.
Xinan Linae7d6372019-09-12 14:42:10 -0700627 configs: a config_reader.Configs object.
628
629 Returns:
630 A build dict.
631 """
632 models_by_board = configs.lab_config.get_cros_model_map()
633 if not models_by_board:
634 return build_dict
635 all_branch_build_dict = {}
636 for (board, model, config, milestone), platform in build_dict.iteritems():
637 uni_build_models = models_by_board.get(board)
638 if uni_build_models is not None and model is None:
639 for uni_build_model in uni_build_models:
640 model_key = (board, uni_build_model, config, milestone)
641 _add_build_dict(all_branch_build_dict, model_key, platform)
642 continue
643 build_key = (board, model, config, milestone)
644 _add_build_dict(all_branch_build_dict, build_key, platform)
645
646 return all_branch_build_dict
647
648
649def _add_build_dict(build_dict, key, value):
650 """A wrapper to add or update an item in build_dict."""
651 cur_manifest = build_dict.get(key)
652 if cur_manifest is None:
653 build_dict[key] = value
654 return
655 build_dict[key] = max(
656 [cur_manifest, value], key=version.LooseVersion)