blob: 940cbbac6254d5d6764a82cdc0825dd0ce56665e [file] [log] [blame]
Chris Masonefad911a2012-03-29 12:30:26 -07001# Copyright (c) 2012 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
Chris Masone67f06d62012-04-12 15:16:56 -07005
Prashanth Balasubramanian1b859622014-10-28 16:02:15 -07006import logging
7import re
8import subprocess
Dan Shia25e0d42015-07-23 15:00:04 -07009
10import base_event
Aviv Keshetd83ef442013-01-16 16:19:35 -080011import deduping_scheduler
Alex Millerc7bcf8b2013-09-07 20:13:20 -070012import driver
Dan Shia25e0d42015-07-23 15:00:04 -070013import manifest_versions
Chris Masone67f06d62012-04-12 15:16:56 -070014from distutils import version
Alex Miller511a9e32012-07-03 09:16:47 -070015from constants import Labels
Dan Shia25e0d42015-07-23 15:00:04 -070016from constants import Builds
Chris Masone96f16632012-04-04 18:36:03 -070017
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070018import common
Fang Dengf08814a2015-08-03 18:12:18 +000019from autotest_lib.server import utils as server_utils
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070020from autotest_lib.server.cros.dynamic_suite import constants
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070021
Dan Shi2121a332016-02-25 14:22:22 -080022OS_TYPE_CROS = 'cros'
23OS_TYPE_BRILLO = 'brillo'
24OS_TYPE_ANDROID = 'android'
25OS_TYPES = {OS_TYPE_CROS, OS_TYPE_BRILLO, OS_TYPE_ANDROID}
26OS_TYPES_LAUNCH_CONTROL = {OS_TYPE_BRILLO, OS_TYPE_ANDROID}
Chris Masone96f16632012-04-04 18:36:03 -070027
28class MalformedConfigEntry(Exception):
29 """Raised to indicate a failure to parse a Task out of a config."""
30 pass
Chris Masonefad911a2012-03-29 12:30:26 -070031
32
Alex Miller0d003572013-03-18 11:51:30 -070033BARE_BRANCHES = ['factory', 'firmware']
Chris Masone67f06d62012-04-12 15:16:56 -070034
35
36def PickBranchName(type, milestone):
Dan Shiaceb91d2013-02-20 12:41:28 -080037 """Pick branch name. If type is among BARE_BRANCHES, return type,
38 otherwise, return milestone.
39
40 @param type: type of the branch, e.g., 'release', 'factory', or 'firmware'
41 @param milestone: CrOS milestone number
42 """
Chris Masone67f06d62012-04-12 15:16:56 -070043 if type in BARE_BRANCHES:
44 return type
45 return milestone
46
47
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070048class TotMilestoneManager(object):
49 """A class capable of converting tot string to milestone numbers.
50
51 This class is used as a cache for the tot milestone, so we don't
52 repeatedly hit google storage for all O(100) tasks in suite
53 scheduler's ini file.
54 """
55
Fang Dengf08814a2015-08-03 18:12:18 +000056 __metaclass__ = server_utils.Singleton
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070057
Dan Shi23245142015-01-22 13:22:28 -080058 # True if suite_scheduler is running for sanity check. When it's set to
59 # True, the code won't make gsutil call to get the actual tot milestone to
60 # avoid dependency on the installation of gsutil to run sanity check.
61 is_sanity = False
62
63
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070064 @staticmethod
65 def _tot_milestone():
66 """Get the tot milestone, eg: R40
67
68 @returns: A string representing the Tot milestone as declared by
69 the LATEST_BUILD_URL, or an empty string if LATEST_BUILD_URL
70 doesn't exist.
71 """
Dan Shi23245142015-01-22 13:22:28 -080072 if TotMilestoneManager.is_sanity:
73 logging.info('suite_scheduler is running for sanity purpose, no '
74 'need to get the actual tot milestone string.')
75 return 'R40'
76
Prashanth Balasubramanian1b859622014-10-28 16:02:15 -070077 cmd = ['gsutil', 'cat', constants.LATEST_BUILD_URL]
78 proc = subprocess.Popen(
79 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
80 stdout, stderr = proc.communicate()
81 if proc.poll():
82 logging.warning('Failed to get latest build: %s', stderr)
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070083 return ''
Prashanth Balasubramanian1b859622014-10-28 16:02:15 -070084 return stdout.split('-')[0]
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070085
86
87 def refresh(self):
88 """Refresh the tot milestone string managed by this class."""
89 self.tot = self._tot_milestone()
90
91
92 def __init__(self):
93 """Initialize a TotMilestoneManager."""
94 self.refresh()
95
96
97 def ConvertTotSpec(self, tot_spec):
98 """Converts a tot spec to the appropriate milestone.
99
100 Assume tot is R40:
101 tot -> R40
102 tot-1 -> R39
103 tot-2 -> R38
104 tot-(any other numbers) -> R40
105
106 With the last option one assumes that a malformed configuration that has
107 'tot' in it, wants at least tot.
108
109 @param tot_spec: A string representing the tot spec.
110 @raises MalformedConfigEntry: If the tot_spec doesn't match the
111 expected format.
112 """
113 tot_spec = tot_spec.lower()
114 match = re.match('(tot)[-]?(1$|2$)?', tot_spec)
115 if not match:
116 raise MalformedConfigEntry(
117 "%s isn't a valid branch spec." % tot_spec)
118 tot_mstone = self.tot
119 num_back = match.groups()[1]
120 if num_back:
121 tot_mstone_num = tot_mstone.lstrip('R')
122 tot_mstone = tot_mstone.replace(
123 tot_mstone_num, str(int(tot_mstone_num)-int(num_back)))
124 return tot_mstone
125
126
Chris Masone013859b2012-04-01 13:45:26 -0700127class Task(object):
Chris Masonefad911a2012-03-29 12:30:26 -0700128 """Represents an entry from the scheduler config. Can schedule itself.
129
130 Each entry from the scheduler config file maps one-to-one to a
Chris Masone013859b2012-04-01 13:45:26 -0700131 Task. Each instance has enough info to schedule itself
Chris Masonefad911a2012-03-29 12:30:26 -0700132 on-demand with the AFE.
133
Aviv Keshet52c7a212015-12-07 15:27:22 -0800134 This class also overrides __hash__() and all comparator methods to enable
Chris Masonefad911a2012-03-29 12:30:26 -0700135 correct use in dicts, sets, etc.
136 """
137
Chris Masone96f16632012-04-04 18:36:03 -0700138
139 @staticmethod
140 def CreateFromConfigSection(config, section):
141 """Create a Task from a section of a config file.
142
143 The section to parse should look like this:
144 [TaskName]
145 suite: suite_to_run # Required
146 run_on: event_on which to run # Required
Dan Shi9f256d92016-01-22 00:09:25 -0800147 hour: integer of the hour to run, only applies to nightly. # Optional
Dan Shiaceb91d2013-02-20 12:41:28 -0800148 branch_specs: factory,firmware,>=R12 or ==R12 # Optional
Chris Masone96f16632012-04-04 18:36:03 -0700149 pool: pool_of_devices # Optional
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700150 num: sharding_factor # int, Optional
Alex Millerf2b57442013-09-07 18:40:02 -0700151 boards: board1, board2 # comma seperated string, Optional
Dan Shi2121a332016-02-25 14:22:22 -0800152 # Settings for Launch Control builds only:
153 os_type: brillo # Type of OS, e.g., cros, brillo, android. Default is
154 cros. Required for android/brillo builds.
155 branches: git_mnc_release # comma separated string of Launch Control
156 branches. Required and only applicable for android/brillo
157 builds.
158 targets: dragonboard-eng # comma separated string of build targets.
159 Required and only applicable for android/brillo builds.
Chris Masone96f16632012-04-04 18:36:03 -0700160
Chris Masone67f06d62012-04-12 15:16:56 -0700161 By default, Tasks run on all release branches, not factory or firmware.
162
Chris Masone96f16632012-04-04 18:36:03 -0700163 @param config: a ForgivingConfigParser.
164 @param section: the section to parse into a Task.
165 @return keyword, Task object pair. One or both will be None on error.
166 @raise MalformedConfigEntry if there's a problem parsing |section|.
167 """
Alex Miller06695022012-07-18 09:31:36 -0700168 if not config.has_section(section):
169 raise MalformedConfigEntry('unknown section %s' % section)
170
Alex Millerf2b57442013-09-07 18:40:02 -0700171 allowed = set(['suite', 'run_on', 'branch_specs', 'pool', 'num',
Dan Shia25e0d42015-07-23 15:00:04 -0700172 'boards', 'file_bugs', 'cros_build_spec',
Dan Shib49bb8b2016-03-01 15:29:27 -0800173 'firmware_rw_build_spec', 'firmware_ro_build_spec',
174 'test_source', 'job_retry', 'hour', 'day', 'branches',
175 'targets', 'os_type'])
Alex Millerbb535e22012-07-11 20:11:33 -0700176 # The parameter of union() is the keys under the section in the config
177 # The union merges this with the allowed set, so if any optional keys
178 # are omitted, then they're filled in. If any extra keys are present,
179 # then they will expand unioned set, causing it to fail the following
180 # comparison against the allowed set.
181 section_headers = allowed.union(dict(config.items(section)).keys())
182 if allowed != section_headers:
183 raise MalformedConfigEntry('unknown entries: %s' %
184 ", ".join(map(str, section_headers.difference(allowed))))
185
Chris Masone96f16632012-04-04 18:36:03 -0700186 keyword = config.getstring(section, 'run_on')
Dan Shi9f256d92016-01-22 00:09:25 -0800187 hour = config.getstring(section, 'hour')
Chris Masone96f16632012-04-04 18:36:03 -0700188 suite = config.getstring(section, 'suite')
Dan Shi2121a332016-02-25 14:22:22 -0800189 branch_specs = config.getstring(section, 'branch_specs')
Chris Masone96f16632012-04-04 18:36:03 -0700190 pool = config.getstring(section, 'pool')
Alex Millerf2b57442013-09-07 18:40:02 -0700191 boards = config.getstring(section, 'boards')
Prashanth B6de2bde2014-03-25 18:45:02 -0700192 file_bugs = config.getboolean(section, 'file_bugs')
Dan Shia25e0d42015-07-23 15:00:04 -0700193 cros_build_spec = config.getstring(section, 'cros_build_spec')
194 firmware_rw_build_spec = config.getstring(
195 section, 'firmware_rw_build_spec')
Dan Shib49bb8b2016-03-01 15:29:27 -0800196 firmware_ro_build_spec = config.getstring(
197 section, 'firmware_ro_build_spec')
Dan Shia25e0d42015-07-23 15:00:04 -0700198 test_source = config.getstring(section, 'test_source')
Dan Shi29a16992015-09-22 11:29:58 -0700199 job_retry = config.getboolean(section, 'job_retry')
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700200 for klass in driver.Driver.EVENT_CLASSES:
201 if klass.KEYWORD == keyword:
202 priority = klass.PRIORITY
203 timeout = klass.TIMEOUT
204 break
205 else:
206 priority = None
207 timeout = None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700208 try:
209 num = config.getint(section, 'num')
210 except ValueError as e:
Dan Shi9f256d92016-01-22 00:09:25 -0800211 raise MalformedConfigEntry("Ill-specified 'num': %r" % e)
Chris Masone96f16632012-04-04 18:36:03 -0700212 if not keyword:
213 raise MalformedConfigEntry('No event to |run_on|.')
214 if not suite:
215 raise MalformedConfigEntry('No |suite|')
Dan Shi9f256d92016-01-22 00:09:25 -0800216 try:
217 hour = config.getint(section, 'hour')
218 except ValueError as e:
219 raise MalformedConfigEntry("Ill-specified 'hour': %r" % e)
220 if hour is not None and (hour < 0 or hour > 23):
221 raise MalformedConfigEntry(
222 '`hour` must be an integer between 0 and 23.')
223 if hour is not None and keyword != 'nightly':
224 raise MalformedConfigEntry(
225 '`hour` is the trigger time that can only apply to nightly '
226 'event.')
Dan Shice1f20a2016-01-25 17:27:40 -0800227
228 try:
229 day = config.getint(section, 'day')
230 except ValueError as e:
231 raise MalformedConfigEntry("Ill-specified 'day': %r" % e)
232 if day is not None and (day < 0 or day > 6):
233 raise MalformedConfigEntry(
234 '`day` must be an integer between 0 and 6, where 0 is for '
235 'Monday and 6 is for Sunday.')
236 if day is not None and keyword != 'weekly':
237 raise MalformedConfigEntry(
238 '`day` is the trigger of the day of a week, that can only '
239 'apply to weekly events.')
240
Chris Masone96f16632012-04-04 18:36:03 -0700241 specs = []
Dan Shi2121a332016-02-25 14:22:22 -0800242 if branch_specs:
243 specs = re.split('\s*,\s*', branch_specs)
Chris Masone96f16632012-04-04 18:36:03 -0700244 Task.CheckBranchSpecs(specs)
Dan Shi2121a332016-02-25 14:22:22 -0800245
246 os_type = config.getstring(section, 'os_type') or OS_TYPE_CROS
247 if os_type not in OS_TYPES:
248 raise MalformedConfigEntry('`os_type` must be one of %s' % OS_TYPES)
249
250 lc_branches = config.getstring(section, 'branches')
251 lc_targets = config.getstring(section, 'targets')
252 if os_type == OS_TYPE_CROS and (lc_branches or lc_targets):
253 raise MalformedConfigEntry(
254 '`branches` and `targets` are only supported for Launch '
255 'Control builds, not ChromeOS builds.')
256 if (os_type in OS_TYPES_LAUNCH_CONTROL and
257 (not lc_branches or not lc_targets)):
258 raise MalformedConfigEntry(
259 '`branches` and `targets` must be specified for Launch '
260 'Control builds.')
261 if os_type in OS_TYPES_LAUNCH_CONTROL and boards:
262 raise MalformedConfigEntry(
263 '`boards` for Launch Control builds are retrieved from '
264 '`targets` setting, it should not be set for Launch '
265 'Control builds.')
266
267 # Extract boards from targets list.
268 if os_type in OS_TYPES_LAUNCH_CONTROL:
269 boards = ''
270 for target in lc_targets.split(','):
271 board_name, _ = server_utils.parse_launch_control_target(
272 target.strip())
273 boards += '%s-%s,' % (os_type, board_name)
274 boards = boards.strip(',')
275
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700276 return keyword, Task(section, suite, specs, pool, num, boards,
Prashanth B6de2bde2014-03-25 18:45:02 -0700277 priority, timeout,
Dan Shia25e0d42015-07-23 15:00:04 -0700278 file_bugs=file_bugs if file_bugs else False,
279 cros_build_spec=cros_build_spec,
280 firmware_rw_build_spec=firmware_rw_build_spec,
Dan Shib49bb8b2016-03-01 15:29:27 -0800281 firmware_ro_build_spec=firmware_ro_build_spec,
Dan Shi9f256d92016-01-22 00:09:25 -0800282 test_source=test_source, job_retry=job_retry,
Dan Shi2121a332016-02-25 14:22:22 -0800283 hour=hour, day=day, os_type=os_type,
284 launch_control_branches=lc_branches,
285 launch_control_targets=lc_targets)
Chris Masone96f16632012-04-04 18:36:03 -0700286
287
288 @staticmethod
289 def CheckBranchSpecs(branch_specs):
290 """Make sure entries in the list branch_specs are correctly formed.
291
Chris Masone67f06d62012-04-12 15:16:56 -0700292 We accept any of BARE_BRANCHES in |branch_specs|, as
Dan Shiaceb91d2013-02-20 12:41:28 -0800293 well as _one_ string of the form '>=RXX' or '==RXX', where 'RXX' is a
Chris Masone96f16632012-04-04 18:36:03 -0700294 CrOS milestone number.
295
296 @param branch_specs: an iterable of branch specifiers.
297 @raise MalformedConfigEntry if there's a problem parsing |branch_specs|.
298 """
299 have_seen_numeric_constraint = False
300 for branch in branch_specs:
Chris Masone67f06d62012-04-12 15:16:56 -0700301 if branch in BARE_BRANCHES:
Chris Masone96f16632012-04-04 18:36:03 -0700302 continue
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700303 if not have_seen_numeric_constraint:
304 #TODO(beeps): Why was <= dropped on the floor?
305 if branch.startswith('>=R') or branch.startswith('==R'):
306 have_seen_numeric_constraint = True
307 elif 'tot' in branch:
308 TotMilestoneManager().ConvertTotSpec(
309 branch[branch.index('tot'):])
310 have_seen_numeric_constraint = True
Chris Masone96f16632012-04-04 18:36:03 -0700311 continue
Chris Masone97cf0a72012-05-16 09:55:52 -0700312 raise MalformedConfigEntry("%s isn't a valid branch spec." % branch)
Chris Masone96f16632012-04-04 18:36:03 -0700313
314
Alex Millerf2b57442013-09-07 18:40:02 -0700315 def __init__(self, name, suite, branch_specs, pool=None, num=None,
Dan Shia25e0d42015-07-23 15:00:04 -0700316 boards=None, priority=None, timeout=None, file_bugs=False,
317 cros_build_spec=None, firmware_rw_build_spec=None,
Dan Shib49bb8b2016-03-01 15:29:27 -0800318 firmware_ro_build_spec=None, test_source=None, job_retry=False,
319 hour=None, day=None, os_type=OS_TYPE_CROS,
320 launch_control_branches=None, launch_control_targets=None):
Chris Masonefad911a2012-03-29 12:30:26 -0700321 """Constructor
322
Chris Masone96f16632012-04-04 18:36:03 -0700323 Given an iterable in |branch_specs|, pre-vetted using CheckBranchSpecs,
324 we'll store them such that _FitsSpec() can be used to check whether a
325 given branch 'fits' with the specifications passed in here.
326 For example, given branch_specs = ['factory', '>=R18'], we'd set things
327 up so that _FitsSpec() would return True for 'factory', or 'RXX'
Dan Shiaceb91d2013-02-20 12:41:28 -0800328 where XX is a number >= 18. Same check is done for branch_specs = [
329 'factory', '==R18'], which limit the test to only one specific branch.
Chris Masone96f16632012-04-04 18:36:03 -0700330
331 Given branch_specs = ['factory', 'firmware'], _FitsSpec()
332 would pass only those two specific strings.
333
334 Example usage:
Chris Masonecc4631d2012-04-20 12:06:39 -0700335 t = Task('Name', 'suite', ['factory', '>=R18'])
Chris Masone96f16632012-04-04 18:36:03 -0700336 t._FitsSpec('factory') # True
337 t._FitsSpec('R19') # True
338 t._FitsSpec('R17') # False
339 t._FitsSpec('firmware') # False
340 t._FitsSpec('goober') # False
341
Dan Shiaceb91d2013-02-20 12:41:28 -0800342 t = Task('Name', 'suite', ['factory', '==R18'])
343 t._FitsSpec('R19') # False, branch does not equal to 18
344 t._FitsSpec('R18') # True
345 t._FitsSpec('R17') # False
346
Dan Shia25e0d42015-07-23 15:00:04 -0700347 cros_build_spec and firmware_rw_build_spec are set for tests require
348 firmware update on the dut. Only one of them can be set.
349 For example:
350 branch_specs: ==tot
351 firmware_rw_build_spec: firmware
352 test_source: cros
353 This will run test using latest build on firmware branch, and the latest
354 ChromeOS build on ToT. The test source build is ChromeOS build.
355
356 branch_specs: firmware
357 cros_build_spec: ==tot-1
358 test_source: firmware_rw
359 This will run test using latest build on firmware branch, and the latest
360 ChromeOS build on dev channel (ToT-1). The test source build is the
361 firmware RW build.
362
Dan Shi59562a82016-01-06 16:05:31 -0800363 branch_specs: ==tot
364 firmware_rw_build_spec: cros
365 test_source: cros
366 This will run test using latest ChromeOS and firmware RW build on ToT.
367 ChromeOS build on ToT. The test source build is ChromeOS build.
368
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700369 @param name: name of this task, e.g. 'NightlyPower'
Chris Masonefad911a2012-03-29 12:30:26 -0700370 @param suite: the name of the suite to run, e.g. 'bvt'
Chris Masone96f16632012-04-04 18:36:03 -0700371 @param branch_specs: a pre-vetted iterable of branch specifiers,
372 e.g. ['>=R18', 'factory']
Chris Masonefad911a2012-03-29 12:30:26 -0700373 @param pool: the pool of machines to use for scheduling purposes.
374 Default: None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700375 @param num: the number of devices across which to shard the test suite.
Aviv Keshetd83ef442013-01-16 16:19:35 -0800376 Type: integer or None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700377 Default: None
Dan Shi9f256d92016-01-22 00:09:25 -0800378 @param boards: A comma separated list of boards to run this task on.
Alex Millerf2b57442013-09-07 18:40:02 -0700379 Default: Run on all boards.
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700380 @param priority: The string name of a priority from
381 client.common_lib.priorities.Priority.
382 @param timeout: The max lifetime of the suite in hours.
Prashanth B6de2bde2014-03-25 18:45:02 -0700383 @param file_bugs: True if bug filing is desired for the suite created
384 for this task.
Dan Shia25e0d42015-07-23 15:00:04 -0700385 @param cros_build_spec: Spec used to determine the ChromeOS build to
386 test with a firmware build, e.g., tot, R41 etc.
Dan Shib49bb8b2016-03-01 15:29:27 -0800387 @param firmware_rw_build_spec: Spec used to determine the firmware RW
388 build test with a ChromeOS build.
389 @param firmware_ro_build_spec: Spec used to determine the firmware RO
390 build test with a ChromeOS build.
Dan Shia25e0d42015-07-23 15:00:04 -0700391 @param test_source: The source of test code when firmware will be
Dan Shib49bb8b2016-03-01 15:29:27 -0800392 updated in the test. The value can be `firmware_rw`,
393 `firmware_ro` or `cros`.
Dan Shi29a16992015-09-22 11:29:58 -0700394 @param job_retry: Set to True to enable job-level retry. Default is
395 False.
Dan Shi9f256d92016-01-22 00:09:25 -0800396 @param hour: An integer specifying the hour that a nightly run should
397 be triggered, default is set to 21.
Dan Shice1f20a2016-01-25 17:27:40 -0800398 @param day: An integer specifying the day of a week that a weekly run
Dan Shi2121a332016-02-25 14:22:22 -0800399 should be triggered, default is set to 5, which is Saturday.
400 @param os_type: Type of OS, e.g., cros, brillo, android. Default is
401 cros. The argument is required for android/brillo builds.
402 @param launch_control_branches: Comma separated string of Launch Control
403 branches. The argument is required and only applicable for
404 android/brillo builds.
405 @param launch_control_targets: Comma separated string of build targets
406 for Launch Control builds. The argument is required and only
407 applicable for android/brillo builds.
Chris Masonefad911a2012-03-29 12:30:26 -0700408 """
Chris Masonecc4631d2012-04-20 12:06:39 -0700409 self._name = name
Chris Masonefad911a2012-03-29 12:30:26 -0700410 self._suite = suite
Chris Masone96f16632012-04-04 18:36:03 -0700411 self._branch_specs = branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700412 self._pool = pool
Aviv Keshetd83ef442013-01-16 16:19:35 -0800413 self._num = num
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700414 self._priority = priority
415 self._timeout = timeout
Prashanth B6de2bde2014-03-25 18:45:02 -0700416 self._file_bugs = file_bugs
Dan Shia25e0d42015-07-23 15:00:04 -0700417 self._cros_build_spec = cros_build_spec
418 self._firmware_rw_build_spec = firmware_rw_build_spec
Dan Shib49bb8b2016-03-01 15:29:27 -0800419 self._firmware_ro_build_spec = firmware_ro_build_spec
Dan Shia25e0d42015-07-23 15:00:04 -0700420 self._test_source = test_source
Dan Shi29a16992015-09-22 11:29:58 -0700421 self._job_retry = job_retry
Dan Shi2121a332016-02-25 14:22:22 -0800422 self._hour = hour
423 self._day = day
424 self._os_type = os_type
425 self._launch_control_branches = (
426 [b.strip() for b in launch_control_branches.split(',')]
427 if launch_control_branches else [])
428 self._launch_control_targets = (
429 [t.strip() for t in launch_control_targets.split(',')]
430 if launch_control_targets else [])
Dan Shia25e0d42015-07-23 15:00:04 -0700431
Dan Shib49bb8b2016-03-01 15:29:27 -0800432 if ((self._firmware_rw_build_spec or self._firmware_ro_build_spec or
433 cros_build_spec) and
434 not self.test_source in [Builds.FIRMWARE_RW, Builds.FIRMWARE_RO,
435 Builds.CROS]):
Dan Shia25e0d42015-07-23 15:00:04 -0700436 raise MalformedConfigEntry(
437 'You must specify the build for test source. It can only '
Dan Shib49bb8b2016-03-01 15:29:27 -0800438 'be `firmware_rw`, `firmware_ro` or `cros`.')
Dan Shia25e0d42015-07-23 15:00:04 -0700439 if self._firmware_rw_build_spec and cros_build_spec:
440 raise MalformedConfigEntry(
441 'You cannot specify both firmware_rw_build_spec and '
442 'cros_build_spec. firmware_rw_build_spec is used to specify'
443 ' a firmware build when the suite requires firmware to be '
Dan Shi59562a82016-01-06 16:05:31 -0800444 'updated in the dut, its value can only be `firmware` or '
445 '`cros`. cros_build_spec is used to specify a ChromeOS '
446 'build when build_specs is set to firmware.')
Dan Shia25e0d42015-07-23 15:00:04 -0700447 if (self._firmware_rw_build_spec and
Dan Shi59562a82016-01-06 16:05:31 -0800448 self._firmware_rw_build_spec not in ['firmware', 'cros']):
Dan Shia25e0d42015-07-23 15:00:04 -0700449 raise MalformedConfigEntry(
Dan Shi59562a82016-01-06 16:05:31 -0800450 'firmware_rw_build_spec can only be empty, firmware or '
451 'cros. It does not support other build type yet.')
Chris Masone96f16632012-04-04 18:36:03 -0700452
453 self._bare_branches = []
Dan Shiaceb91d2013-02-20 12:41:28 -0800454 self._version_equal_constraint = False
Dan Shibde10772015-08-18 10:15:58 -0700455 self._version_gte_constraint = False
456 self._version_lte_constraint = False
Chris Masone67f06d62012-04-12 15:16:56 -0700457 if not branch_specs:
458 # Any milestone is OK.
459 self._numeric_constraint = version.LooseVersion('0')
460 else:
461 self._numeric_constraint = None
462 for spec in branch_specs:
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700463 if 'tot' in spec.lower():
464 tot_str = spec[spec.index('tot'):]
465 spec = spec.replace(
466 tot_str, TotMilestoneManager().ConvertTotSpec(
Dan Shibde10772015-08-18 10:15:58 -0700467 tot_str))
Chris Masone67f06d62012-04-12 15:16:56 -0700468 if spec.startswith('>='):
469 self._numeric_constraint = version.LooseVersion(
Dan Shibde10772015-08-18 10:15:58 -0700470 spec.lstrip('>=R'))
471 self._version_gte_constraint = True
472 elif spec.startswith('<='):
473 self._numeric_constraint = version.LooseVersion(
474 spec.lstrip('<=R'))
475 self._version_lte_constraint = True
Dan Shiaceb91d2013-02-20 12:41:28 -0800476 elif spec.startswith('=='):
477 self._version_equal_constraint = True
478 self._numeric_constraint = version.LooseVersion(
Dan Shibde10772015-08-18 10:15:58 -0700479 spec.lstrip('==R'))
Chris Masone67f06d62012-04-12 15:16:56 -0700480 else:
481 self._bare_branches.append(spec)
Alex Millerf2b57442013-09-07 18:40:02 -0700482
Aviv Keshet52c7a212015-12-07 15:27:22 -0800483 # Since we expect __hash__() and other comparator methods to be used
Chris Masonefad911a2012-03-29 12:30:26 -0700484 # frequently by set operations, and they use str() a lot, pre-compute
485 # the string representation of this object.
Aviv Keshet8ce3c982013-01-24 09:23:13 -0800486 if num is None:
487 numStr = '[Default num]'
488 else:
489 numStr = '%d' % num
Alex Millerf2b57442013-09-07 18:40:02 -0700490
491 if boards is None:
492 self._boards = set()
493 boardsStr = '[All boards]'
494 else:
495 self._boards = set([x.strip() for x in boards.split(',')])
496 boardsStr = boards
497
Dan Shi2121a332016-02-25 14:22:22 -0800498 if os_type == OS_TYPE_CROS:
499 self._str = ('%s: %s on %s with pool %s, boards [%s], file_bugs = '
500 '%s across %s machines.' %
501 (self.__class__.__name__, suite, branch_specs, pool,
502 boardsStr, self._file_bugs, numStr))
503 else:
504 self._str = ('%s: %s on branches %s and targets %s with pool %s, '
505 'boards [%s], file_bugs = %s across %s machines.' %
506 (self.__class__.__name__, suite,
507 launch_control_branches, launch_control_targets,
508 pool, boardsStr, self._file_bugs, numStr))
Chris Masone96f16632012-04-04 18:36:03 -0700509
510
511 def _FitsSpec(self, branch):
512 """Checks if a branch is deemed OK by this instance's branch specs.
513
514 When called on a branch name, will return whether that branch
Dan Shiaceb91d2013-02-20 12:41:28 -0800515 'fits' the specifications stored in self._bare_branches,
Dan Shibde10772015-08-18 10:15:58 -0700516 self._numeric_constraint, self._version_equal_constraint,
517 self._version_gte_constraint and self._version_lte_constraint.
Chris Masone96f16632012-04-04 18:36:03 -0700518
519 @param branch: the branch to check.
520 @return True if b 'fits' with stored specs, False otherwise.
521 """
Chris Masone657e1552012-05-30 17:06:20 -0700522 if branch in BARE_BRANCHES:
523 return branch in self._bare_branches
Dan Shiaceb91d2013-02-20 12:41:28 -0800524 if self._numeric_constraint:
525 if self._version_equal_constraint:
526 return version.LooseVersion(branch) == self._numeric_constraint
Dan Shibde10772015-08-18 10:15:58 -0700527 elif self._version_gte_constraint:
528 return version.LooseVersion(branch) >= self._numeric_constraint
529 elif self._version_lte_constraint:
530 return version.LooseVersion(branch) <= self._numeric_constraint
Dan Shiaceb91d2013-02-20 12:41:28 -0800531 else:
Dan Shibde10772015-08-18 10:15:58 -0700532 # Default to great or equal constraint.
Dan Shiaceb91d2013-02-20 12:41:28 -0800533 return version.LooseVersion(branch) >= self._numeric_constraint
534 else:
535 return False
Chris Masonefad911a2012-03-29 12:30:26 -0700536
537
538 @property
Alex Miller9979b5a2012-11-01 17:36:12 -0700539 def name(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800540 """Name of this task, e.g. 'NightlyPower'."""
Alex Miller9979b5a2012-11-01 17:36:12 -0700541 return self._name
542
543
544 @property
Chris Masonefad911a2012-03-29 12:30:26 -0700545 def suite(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800546 """Name of the suite to run, e.g. 'bvt'."""
Chris Masonefad911a2012-03-29 12:30:26 -0700547 return self._suite
548
549
550 @property
Chris Masone96f16632012-04-04 18:36:03 -0700551 def branch_specs(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800552 """a pre-vetted iterable of branch specifiers,
553 e.g. ['>=R18', 'factory']."""
Chris Masone96f16632012-04-04 18:36:03 -0700554 return self._branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700555
556
557 @property
Chris Masone3fba86f2012-04-03 10:06:56 -0700558 def pool(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800559 """The pool of machines to use for scheduling purposes."""
Chris Masone3fba86f2012-04-03 10:06:56 -0700560 return self._pool
Chris Masonefad911a2012-03-29 12:30:26 -0700561
562
Alex Miller9979b5a2012-11-01 17:36:12 -0700563 @property
564 def num(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800565 """The number of devices across which to shard the test suite.
566 Type: integer or None"""
Alex Miller9979b5a2012-11-01 17:36:12 -0700567 return self._num
568
569
Alex Millerf2b57442013-09-07 18:40:02 -0700570 @property
571 def boards(self):
572 """The boards on which to run this suite.
573 Type: Iterable of strings"""
574 return self._boards
575
576
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700577 @property
578 def priority(self):
579 """The priority of the suite"""
580 return self._priority
581
582
583 @property
584 def timeout(self):
585 """The maximum lifetime of the suite in hours."""
586 return self._timeout
587
588
Dan Shia25e0d42015-07-23 15:00:04 -0700589 @property
590 def cros_build_spec(self):
591 """The build spec of ChromeOS to test with a firmware build."""
592 return self._cros_build_spec
593
594
595 @property
596 def firmware_rw_build_spec(self):
Dan Shib49bb8b2016-03-01 15:29:27 -0800597 """The build spec of RW firmware to test with a ChromeOS build.
598
599 The value can be firmware or cros.
600 """
Dan Shia25e0d42015-07-23 15:00:04 -0700601 return self._firmware_rw_build_spec
602
603
604 @property
Dan Shib49bb8b2016-03-01 15:29:27 -0800605 def firmware_ro_build_spec(self):
606 """The build spec of RO firmware to test with a ChromeOS build.
607
608 The value can be stable, firmware or cros, where stable is the stable
609 firmware build retrieved from stable_version table.
610 """
611 return self._firmware_ro_build_spec
612
613
614 @property
Dan Shia25e0d42015-07-23 15:00:04 -0700615 def test_source(self):
Dan Shib49bb8b2016-03-01 15:29:27 -0800616 """Source of the test code, value can be `firmware_rw`, `firmware_ro` or
617 `cros`."""
Dan Shia25e0d42015-07-23 15:00:04 -0700618 return self._test_source
619
620
Dan Shi2121a332016-02-25 14:22:22 -0800621 @property
622 def hour(self):
623 """An integer specifying the hour that a nightly run should be triggered
624 """
625 return self._hour
626
627
628 @property
629 def day(self):
630 """An integer specifying the day of a week that a weekly run should be
631 triggered"""
632 return self._day
633
634
635 @property
636 def os_type(self):
637 """Type of OS, e.g., cros, brillo, android."""
638 return self._os_type
639
640
641 @property
642 def launch_control_branches(self):
643 """A list of Launch Control builds."""
644 return self._launch_control_branches
645
646
647 @property
648 def launch_control_targets(self):
649 """A list of Launch Control targets."""
650 return self._launch_control_targets
651
652
Chris Masonefad911a2012-03-29 12:30:26 -0700653 def __str__(self):
654 return self._str
655
656
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700657 def __repr__(self):
658 return self._str
659
660
Chris Masonefad911a2012-03-29 12:30:26 -0700661 def __lt__(self, other):
662 return str(self) < str(other)
663
664
665 def __le__(self, other):
666 return str(self) <= str(other)
667
668
669 def __eq__(self, other):
670 return str(self) == str(other)
671
672
673 def __ne__(self, other):
674 return str(self) != str(other)
675
676
677 def __gt__(self, other):
678 return str(self) > str(other)
679
680
681 def __ge__(self, other):
682 return str(self) >= str(other)
683
684
685 def __hash__(self):
686 """Allows instances to be correctly deduped when used in a set."""
687 return hash(str(self))
688
689
Dan Shia25e0d42015-07-23 15:00:04 -0700690 def _GetCrOSBuild(self, mv, board):
691 """Get the ChromeOS build name to test with firmware build.
692
693 The ChromeOS build to be used is determined by `self.cros_build_spec`.
694 Its value can be:
695 tot: use the latest ToT build.
696 tot-x: use the latest build in x milestone before ToT.
697 Rxx: use the latest build on xx milestone.
698
699 @param board: the board against which to run self._suite.
700 @param mv: an instance of manifest_versions.ManifestVersions.
701
702 @return: The ChromeOS build name to test with firmware build.
703
704 """
705 if not self.cros_build_spec:
706 return None
707 if self.cros_build_spec.startswith('tot'):
708 milestone = TotMilestoneManager().ConvertTotSpec(
709 self.cros_build_spec)[1:]
710 elif self.cros_build_spec.startswith('R'):
711 milestone = self.cros_build_spec[1:]
712 milestone, latest_manifest = mv.GetLatestManifest(
713 board, 'release', milestone=milestone)
714 latest_build = base_event.BuildName(board, 'release', milestone,
715 latest_manifest)
716 logging.debug('Found latest build of %s for spec %s: %s',
717 board, self.cros_build_spec, latest_build)
718 return latest_build
719
720
Dan Shib49bb8b2016-03-01 15:29:27 -0800721 def _GetFirmwareBuild(self, spec, mv, board):
722 """Get the firmware build name to test with ChromeOS build.
Dan Shia25e0d42015-07-23 15:00:04 -0700723
Dan Shib49bb8b2016-03-01 15:29:27 -0800724 @param spec: build spec for RO or RW firmware, e.g., firmware, cros
Dan Shia25e0d42015-07-23 15:00:04 -0700725 @param mv: an instance of manifest_versions.ManifestVersions.
726 @param board: the board against which to run self._suite.
Dan Shia25e0d42015-07-23 15:00:04 -0700727
Dan Shib49bb8b2016-03-01 15:29:27 -0800728 @return: The firmware build name to test with ChromeOS build.
Dan Shia25e0d42015-07-23 15:00:04 -0700729 """
Dan Shib49bb8b2016-03-01 15:29:27 -0800730 if spec == 'stable':
731 # TODO(crbug.com/577316): Query stable RO firmware.
732 raise NotImplementedError('`stable` RO firmware build is not '
733 'supported yet.')
734 if not spec:
Dan Shia25e0d42015-07-23 15:00:04 -0700735 return None
Dan Shib49bb8b2016-03-01 15:29:27 -0800736 # build_type is the build type of the firmware build, e.g., factory,
737 # firmware or release. If spec is set to cros, build type should be
738 # mapped to release.
739 build_type = 'release' if spec == 'cros' else spec
Dan Shia25e0d42015-07-23 15:00:04 -0700740 latest_milestone, latest_manifest = mv.GetLatestManifest(
741 board, build_type)
742 latest_build = base_event.BuildName(board, build_type, latest_milestone,
743 latest_manifest)
744 logging.debug('Found latest firmware build of %s for spec %s: %s',
Dan Shib49bb8b2016-03-01 15:29:27 -0800745 board, spec, latest_build)
Dan Shia25e0d42015-07-23 15:00:04 -0700746 return latest_build
747
748
Alex Miller7ce1b362012-07-10 09:24:10 -0700749 def AvailableHosts(self, scheduler, board):
750 """Query what hosts are able to run a test on a board and pool
751 combination.
Alex Miller511a9e32012-07-03 09:16:47 -0700752
753 @param scheduler: an instance of DedupingScheduler, as defined in
754 deduping_scheduler.py
755 @param board: the board against which one wants to run the test.
Alex Miller7ce1b362012-07-10 09:24:10 -0700756 @return The list of hosts meeting the board and pool requirements,
757 or None if no hosts were found."""
Alex Millerf2b57442013-09-07 18:40:02 -0700758 if self._boards and board not in self._boards:
759 return []
760
Alex Miller511a9e32012-07-03 09:16:47 -0700761 labels = [Labels.BOARD_PREFIX + board]
762 if self._pool:
Chris Masonecd214e02012-07-10 16:22:10 -0700763 labels.append(Labels.POOL_PREFIX + self._pool)
Alex Miller511a9e32012-07-03 09:16:47 -0700764
Prashanth B6de2bde2014-03-25 18:45:02 -0700765 return scheduler.CheckHostsExist(multiple_labels=labels)
Alex Miller511a9e32012-07-03 09:16:47 -0700766
767
Alex Millerd621cf22012-07-11 13:57:10 -0700768 def ShouldHaveAvailableHosts(self):
769 """As a sanity check, return true if we know for certain that
770 we should be able to schedule this test. If we claim this test
771 should be able to run, and it ends up not being scheduled, then
772 a warning will be reported.
773
774 @return True if this test should be able to run, False otherwise.
775 """
776 return self._pool == 'bvt'
777
778
Dan Shi2121a332016-02-25 14:22:22 -0800779 def _ScheduleSuite(self, scheduler, cros_build, firmware_rw_build,
Dan Shib49bb8b2016-03-01 15:29:27 -0800780 firmware_ro_build, test_source_build,
781 launch_control_build, board, force, run_prod_code=False):
Dan Shi2121a332016-02-25 14:22:22 -0800782 """Try to schedule a suite with given build and board information.
783
784 @param scheduler: an instance of DedupingScheduler, as defined in
785 deduping_scheduler.py
786 @oaran build: Build to run suite for, e.g., 'daisy-release/R18-1655.0.0'
787 and 'git_mnc_release/shamu-eng/123'.
788 @param firmware_rw_build: Firmware RW build to run test with.
Dan Shib49bb8b2016-03-01 15:29:27 -0800789 @param firmware_ro_build: Firmware RO build to run test with.
Dan Shi2121a332016-02-25 14:22:22 -0800790 @param test_source_build: Test source build, used for server-side
791 packaging.
792 @param launch_control_build: Name of a Launch Control build, e.g.,
793 'git_mnc_release/shamu-eng/123'
794 @param board: the board against which to run self._suite.
795 @param force: Always schedule the suite.
796 @param run_prod_code: If True, the suite will run the test code that
797 lives in prod aka the test code currently on the
798 lab servers. If False, the control files and test
799 code for this suite run will be retrieved from the
800 build artifacts. Default is False.
801 """
802 test_source_build_msg = (
803 ' Test source build is %s.' % test_source_build
Dan Shi8446fac2016-03-02 22:07:39 -0800804 if test_source_build else '')
Dan Shi2121a332016-02-25 14:22:22 -0800805 firmware_rw_build_msg = (
806 ' Firmware RW build is %s.' % firmware_rw_build
Dan Shi8446fac2016-03-02 22:07:39 -0800807 if firmware_rw_build else '')
Dan Shib49bb8b2016-03-01 15:29:27 -0800808 firmware_ro_build_msg = (
809 ' Firmware RO build is %s.' % firmware_ro_build
Dan Shi8446fac2016-03-02 22:07:39 -0800810 if firmware_ro_build else '')
Dan Shi2121a332016-02-25 14:22:22 -0800811 build_string = cros_build or launch_control_build
Dan Shib49bb8b2016-03-01 15:29:27 -0800812 logging.debug('Schedule %s for build %s.%s%s%s',
Dan Shi2121a332016-02-25 14:22:22 -0800813 self._suite, build_string, test_source_build_msg,
Dan Shib49bb8b2016-03-01 15:29:27 -0800814 firmware_rw_build_msg, firmware_ro_build_msg)
Dan Shi2121a332016-02-25 14:22:22 -0800815
816 if not scheduler.ScheduleSuite(
817 self._suite, board, cros_build, self._pool, self._num,
818 self._priority, self._timeout, force,
819 file_bugs=self._file_bugs,
820 firmware_rw_build=firmware_rw_build,
Dan Shib49bb8b2016-03-01 15:29:27 -0800821 firmware_ro_build=firmware_ro_build,
Dan Shi2121a332016-02-25 14:22:22 -0800822 test_source_build=test_source_build,
823 job_retry=self._job_retry,
824 launch_control_build=launch_control_build,
825 run_prod_code=run_prod_code):
826 logging.info('Skipping scheduling %s on %s for %s',
827 self._suite, build_string, board)
828
829
830 def _Run_CrOS_Builds(self, scheduler, branch_builds, board, force=False,
831 mv=None):
832 """Run this task for CrOS builds. Returns False if it should be
833 destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -0700834
Chris Masone013859b2012-04-01 13:45:26 -0700835 Execute this task. Attempt to schedule the associated suite.
836 Return True if this task should be kept around, False if it
837 should be destroyed. This allows for one-shot Tasks.
Chris Masonefad911a2012-03-29 12:30:26 -0700838
839 @param scheduler: an instance of DedupingScheduler, as defined in
840 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -0700841 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -0700842 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -0700843 {'R18': ['x86-alex-release/R18-1655.0.0'],
844 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -0700845 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -0700846 @param force: Always schedule the suite.
Dan Shia25e0d42015-07-23 15:00:04 -0700847 @param mv: an instance of manifest_versions.ManifestVersions.
848
Chris Masone013859b2012-04-01 13:45:26 -0700849 @return True if the task should be kept, False if not
Dan Shia25e0d42015-07-23 15:00:04 -0700850
Chris Masonefad911a2012-03-29 12:30:26 -0700851 """
Chris Masonea3a38172012-05-14 15:19:56 -0700852 logging.info('Running %s on %s', self._name, board)
Dan Shia25e0d42015-07-23 15:00:04 -0700853 is_firmware_build = 'firmware' in self.branch_specs
Dan Shib49bb8b2016-03-01 15:29:27 -0800854
855 # firmware_xx_build is only needed if firmware_xx_build_spec is given.
Dan Shia25e0d42015-07-23 15:00:04 -0700856 firmware_rw_build = None
Dan Shib49bb8b2016-03-01 15:29:27 -0800857 firmware_ro_build = None
Dan Shia25e0d42015-07-23 15:00:04 -0700858 try:
859 if is_firmware_build:
860 # When build specified in branch_specs is a firmware build,
861 # we need a ChromeOS build to test with the firmware build.
862 cros_build = self._GetCrOSBuild(mv, board)
Dan Shib49bb8b2016-03-01 15:29:27 -0800863 elif self.firmware_rw_build_spec or self.firmware_ro_build_spec:
864 # When firmware_xx_build_spec is specified, the test involves
865 # updating the RW firmware by firmware build specified in
866 # firmware_xx_build_spec.
867 firmware_rw_build = self._GetFirmwareBuild(
868 self.firmware_rw_build_spec, mv, board)
869 firmware_ro_build = self._GetFirmwareBuild(
870 self.firmware_ro_build_spec, mv, board)
Dan Shia25e0d42015-07-23 15:00:04 -0700871 except manifest_versions.QueryException as e:
872 logging.error(e)
873 logging.error('Running %s on %s is failed. Failed to find build '
874 'required to run the suite.', self._name, board)
875 return False
876
Chris Masone96f16632012-04-04 18:36:03 -0700877 builds = []
Chris Masonefe5a5092012-04-11 18:29:07 -0700878 for branch, build in branch_builds.iteritems():
Chris Masonea3a38172012-05-14 15:19:56 -0700879 logging.info('Checking if %s fits spec %r',
880 branch, self.branch_specs)
Chris Masone96f16632012-04-04 18:36:03 -0700881 if self._FitsSpec(branch):
Dan Shibde10772015-08-18 10:15:58 -0700882 logging.debug('Build %s fits the spec.', build)
Chris Masone05b19442012-04-17 13:37:55 -0700883 builds.extend(build)
Chris Masone96f16632012-04-04 18:36:03 -0700884 for build in builds:
Chris Masone3fba86f2012-04-03 10:06:56 -0700885 try:
Dan Shia25e0d42015-07-23 15:00:04 -0700886 if is_firmware_build:
887 firmware_rw_build = build
888 else:
889 cros_build = build
890 if self.test_source == Builds.FIRMWARE_RW:
891 test_source_build = firmware_rw_build
892 elif self.test_source == Builds.CROS:
893 test_source_build = cros_build
894 else:
895 test_source_build = None
Dan Shi2121a332016-02-25 14:22:22 -0800896 self._ScheduleSuite(scheduler, cros_build, firmware_rw_build,
Dan Shib49bb8b2016-03-01 15:29:27 -0800897 firmware_ro_build, test_source_build,
898 None, board, force)
Chris Masone3fba86f2012-04-03 10:06:56 -0700899 except deduping_scheduler.DedupingSchedulerException as e:
900 logging.error(e)
Chris Masonefad911a2012-03-29 12:30:26 -0700901 return True
902
903
Dan Shi2121a332016-02-25 14:22:22 -0800904 def _Run_LaunchControl_Builds(self, scheduler, launch_control_builds, board,
905 force=False):
906 """Run this task. Returns False if it should be destroyed.
907
908 Execute this task. Attempt to schedule the associated suite.
909 Return True if this task should be kept around, False if it
910 should be destroyed. This allows for one-shot Tasks.
911
912 @param scheduler: an instance of DedupingScheduler, as defined in
913 deduping_scheduler.py
914 @param launch_control_builds: A list of Launch Control builds.
915 @param board: the board against which to run self._suite.
916 @param force: Always schedule the suite.
917
918 @return True if the task should be kept, False if not
919
920 """
921 logging.info('Running %s on %s', self._name, board)
922 for build in launch_control_builds:
923 try:
Dan Shi8446fac2016-03-02 22:07:39 -0800924 self._ScheduleSuite(scheduler, None, None, None,
925 test_source_build=build,
Dan Shi2121a332016-02-25 14:22:22 -0800926 launch_control_build=build, board=board,
927 force=force, run_prod_code=True)
928 except deduping_scheduler.DedupingSchedulerException as e:
929 logging.error(e)
930 return True
931
932
933 def Run(self, scheduler, branch_builds, board, force=False, mv=None,
934 launch_control_builds=None):
935 """Run this task. Returns False if it should be destroyed.
936
937 Execute this task. Attempt to schedule the associated suite.
938 Return True if this task should be kept around, False if it
939 should be destroyed. This allows for one-shot Tasks.
940
941 @param scheduler: an instance of DedupingScheduler, as defined in
942 deduping_scheduler.py
943 @param branch_builds: a dict mapping branch name to the build(s) to
944 install for that branch, e.g.
945 {'R18': ['x86-alex-release/R18-1655.0.0'],
946 'R19': ['x86-alex-release/R19-2077.0.0']}
947 @param board: the board against which to run self._suite.
948 @param force: Always schedule the suite.
949 @param mv: an instance of manifest_versions.ManifestVersions.
950 @param launch_control_builds: A list of Launch Control builds.
951
952 @return True if the task should be kept, False if not
953
954 """
955 if ((self._os_type == OS_TYPE_CROS and not branch_builds) or
956 (self._os_type != OS_TYPE_CROS and not launch_control_builds)):
957 logging.debug('No build to run, skip running %s on %s.', self._name,
958 board)
959 # Return True so the task will be kept, as the given build and board
960 # do not match.
961 return True
962
963 if self._os_type == OS_TYPE_CROS:
964 return self._Run_CrOS_Builds(
965 scheduler, branch_builds, board, force, mv)
966 else:
967 return self._Run_LaunchControl_Builds(
968 scheduler, launch_control_builds, board, force)
969
970
Chris Masone013859b2012-04-01 13:45:26 -0700971class OneShotTask(Task):
972 """A Task that can be run only once. Can schedule itself."""
Chris Masonefad911a2012-03-29 12:30:26 -0700973
974
Dan Shi2121a332016-02-25 14:22:22 -0800975 def Run(self, scheduler, branch_builds, board, force=False, mv=None,
976 launch_control_builds=None):
Chris Masone013859b2012-04-01 13:45:26 -0700977 """Run this task. Returns False, indicating it should be destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -0700978
Chris Masone013859b2012-04-01 13:45:26 -0700979 Run this task. Attempt to schedule the associated suite.
980 Return False, indicating to the caller that it should discard this task.
Chris Masonefad911a2012-03-29 12:30:26 -0700981
982 @param scheduler: an instance of DedupingScheduler, as defined in
983 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -0700984 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -0700985 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -0700986 {'R18': ['x86-alex-release/R18-1655.0.0'],
987 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -0700988 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -0700989 @param force: Always schedule the suite.
Dan Shia25e0d42015-07-23 15:00:04 -0700990 @param mv: an instance of manifest_versions.ManifestVersions.
Dan Shi2121a332016-02-25 14:22:22 -0800991 @param launch_control_builds: A list of Launch Control builds.
Dan Shia25e0d42015-07-23 15:00:04 -0700992
Chris Masonefad911a2012-03-29 12:30:26 -0700993 @return False
Dan Shia25e0d42015-07-23 15:00:04 -0700994
Chris Masonefad911a2012-03-29 12:30:26 -0700995 """
Dan Shia25e0d42015-07-23 15:00:04 -0700996 super(OneShotTask, self).Run(scheduler, branch_builds, board, force,
Dan Shi2121a332016-02-25 14:22:22 -0800997 mv, launch_control_builds)
Chris Masonefad911a2012-03-29 12:30:26 -0700998 return False