blob: e4ec6699405064e97c00444da6b398baffce3140 [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 Shi9f256d92016-01-22 00:09:25 -0800173 'firmware_rw_build_spec', 'test_source', 'job_retry',
Dan Shi2121a332016-02-25 14:22:22 -0800174 'hour', 'day', 'branches', 'targets', 'os_type'])
Alex Millerbb535e22012-07-11 20:11:33 -0700175 # The parameter of union() is the keys under the section in the config
176 # The union merges this with the allowed set, so if any optional keys
177 # are omitted, then they're filled in. If any extra keys are present,
178 # then they will expand unioned set, causing it to fail the following
179 # comparison against the allowed set.
180 section_headers = allowed.union(dict(config.items(section)).keys())
181 if allowed != section_headers:
182 raise MalformedConfigEntry('unknown entries: %s' %
183 ", ".join(map(str, section_headers.difference(allowed))))
184
Chris Masone96f16632012-04-04 18:36:03 -0700185 keyword = config.getstring(section, 'run_on')
Dan Shi9f256d92016-01-22 00:09:25 -0800186 hour = config.getstring(section, 'hour')
Chris Masone96f16632012-04-04 18:36:03 -0700187 suite = config.getstring(section, 'suite')
Dan Shi2121a332016-02-25 14:22:22 -0800188 branch_specs = config.getstring(section, 'branch_specs')
Chris Masone96f16632012-04-04 18:36:03 -0700189 pool = config.getstring(section, 'pool')
Alex Millerf2b57442013-09-07 18:40:02 -0700190 boards = config.getstring(section, 'boards')
Prashanth B6de2bde2014-03-25 18:45:02 -0700191 file_bugs = config.getboolean(section, 'file_bugs')
Dan Shia25e0d42015-07-23 15:00:04 -0700192 cros_build_spec = config.getstring(section, 'cros_build_spec')
193 firmware_rw_build_spec = config.getstring(
194 section, 'firmware_rw_build_spec')
195 test_source = config.getstring(section, 'test_source')
Dan Shi29a16992015-09-22 11:29:58 -0700196 job_retry = config.getboolean(section, 'job_retry')
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700197 for klass in driver.Driver.EVENT_CLASSES:
198 if klass.KEYWORD == keyword:
199 priority = klass.PRIORITY
200 timeout = klass.TIMEOUT
201 break
202 else:
203 priority = None
204 timeout = None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700205 try:
206 num = config.getint(section, 'num')
207 except ValueError as e:
Dan Shi9f256d92016-01-22 00:09:25 -0800208 raise MalformedConfigEntry("Ill-specified 'num': %r" % e)
Chris Masone96f16632012-04-04 18:36:03 -0700209 if not keyword:
210 raise MalformedConfigEntry('No event to |run_on|.')
211 if not suite:
212 raise MalformedConfigEntry('No |suite|')
Dan Shi9f256d92016-01-22 00:09:25 -0800213 try:
214 hour = config.getint(section, 'hour')
215 except ValueError as e:
216 raise MalformedConfigEntry("Ill-specified 'hour': %r" % e)
217 if hour is not None and (hour < 0 or hour > 23):
218 raise MalformedConfigEntry(
219 '`hour` must be an integer between 0 and 23.')
220 if hour is not None and keyword != 'nightly':
221 raise MalformedConfigEntry(
222 '`hour` is the trigger time that can only apply to nightly '
223 'event.')
Dan Shice1f20a2016-01-25 17:27:40 -0800224
225 try:
226 day = config.getint(section, 'day')
227 except ValueError as e:
228 raise MalformedConfigEntry("Ill-specified 'day': %r" % e)
229 if day is not None and (day < 0 or day > 6):
230 raise MalformedConfigEntry(
231 '`day` must be an integer between 0 and 6, where 0 is for '
232 'Monday and 6 is for Sunday.')
233 if day is not None and keyword != 'weekly':
234 raise MalformedConfigEntry(
235 '`day` is the trigger of the day of a week, that can only '
236 'apply to weekly events.')
237
Chris Masone96f16632012-04-04 18:36:03 -0700238 specs = []
Dan Shi2121a332016-02-25 14:22:22 -0800239 if branch_specs:
240 specs = re.split('\s*,\s*', branch_specs)
Chris Masone96f16632012-04-04 18:36:03 -0700241 Task.CheckBranchSpecs(specs)
Dan Shi2121a332016-02-25 14:22:22 -0800242
243 os_type = config.getstring(section, 'os_type') or OS_TYPE_CROS
244 if os_type not in OS_TYPES:
245 raise MalformedConfigEntry('`os_type` must be one of %s' % OS_TYPES)
246
247 lc_branches = config.getstring(section, 'branches')
248 lc_targets = config.getstring(section, 'targets')
249 if os_type == OS_TYPE_CROS and (lc_branches or lc_targets):
250 raise MalformedConfigEntry(
251 '`branches` and `targets` are only supported for Launch '
252 'Control builds, not ChromeOS builds.')
253 if (os_type in OS_TYPES_LAUNCH_CONTROL and
254 (not lc_branches or not lc_targets)):
255 raise MalformedConfigEntry(
256 '`branches` and `targets` must be specified for Launch '
257 'Control builds.')
258 if os_type in OS_TYPES_LAUNCH_CONTROL and boards:
259 raise MalformedConfigEntry(
260 '`boards` for Launch Control builds are retrieved from '
261 '`targets` setting, it should not be set for Launch '
262 'Control builds.')
263
264 # Extract boards from targets list.
265 if os_type in OS_TYPES_LAUNCH_CONTROL:
266 boards = ''
267 for target in lc_targets.split(','):
268 board_name, _ = server_utils.parse_launch_control_target(
269 target.strip())
270 boards += '%s-%s,' % (os_type, board_name)
271 boards = boards.strip(',')
272
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700273 return keyword, Task(section, suite, specs, pool, num, boards,
Prashanth B6de2bde2014-03-25 18:45:02 -0700274 priority, timeout,
Dan Shia25e0d42015-07-23 15:00:04 -0700275 file_bugs=file_bugs if file_bugs else False,
276 cros_build_spec=cros_build_spec,
277 firmware_rw_build_spec=firmware_rw_build_spec,
Dan Shi9f256d92016-01-22 00:09:25 -0800278 test_source=test_source, job_retry=job_retry,
Dan Shi2121a332016-02-25 14:22:22 -0800279 hour=hour, day=day, os_type=os_type,
280 launch_control_branches=lc_branches,
281 launch_control_targets=lc_targets)
Chris Masone96f16632012-04-04 18:36:03 -0700282
283
284 @staticmethod
285 def CheckBranchSpecs(branch_specs):
286 """Make sure entries in the list branch_specs are correctly formed.
287
Chris Masone67f06d62012-04-12 15:16:56 -0700288 We accept any of BARE_BRANCHES in |branch_specs|, as
Dan Shiaceb91d2013-02-20 12:41:28 -0800289 well as _one_ string of the form '>=RXX' or '==RXX', where 'RXX' is a
Chris Masone96f16632012-04-04 18:36:03 -0700290 CrOS milestone number.
291
292 @param branch_specs: an iterable of branch specifiers.
293 @raise MalformedConfigEntry if there's a problem parsing |branch_specs|.
294 """
295 have_seen_numeric_constraint = False
296 for branch in branch_specs:
Chris Masone67f06d62012-04-12 15:16:56 -0700297 if branch in BARE_BRANCHES:
Chris Masone96f16632012-04-04 18:36:03 -0700298 continue
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700299 if not have_seen_numeric_constraint:
300 #TODO(beeps): Why was <= dropped on the floor?
301 if branch.startswith('>=R') or branch.startswith('==R'):
302 have_seen_numeric_constraint = True
303 elif 'tot' in branch:
304 TotMilestoneManager().ConvertTotSpec(
305 branch[branch.index('tot'):])
306 have_seen_numeric_constraint = True
Chris Masone96f16632012-04-04 18:36:03 -0700307 continue
Chris Masone97cf0a72012-05-16 09:55:52 -0700308 raise MalformedConfigEntry("%s isn't a valid branch spec." % branch)
Chris Masone96f16632012-04-04 18:36:03 -0700309
310
Alex Millerf2b57442013-09-07 18:40:02 -0700311 def __init__(self, name, suite, branch_specs, pool=None, num=None,
Dan Shia25e0d42015-07-23 15:00:04 -0700312 boards=None, priority=None, timeout=None, file_bugs=False,
313 cros_build_spec=None, firmware_rw_build_spec=None,
Dan Shi2121a332016-02-25 14:22:22 -0800314 test_source=None, job_retry=False, hour=None, day=None,
315 os_type=OS_TYPE_CROS, launch_control_branches=None,
316 launch_control_targets=None):
Chris Masonefad911a2012-03-29 12:30:26 -0700317 """Constructor
318
Chris Masone96f16632012-04-04 18:36:03 -0700319 Given an iterable in |branch_specs|, pre-vetted using CheckBranchSpecs,
320 we'll store them such that _FitsSpec() can be used to check whether a
321 given branch 'fits' with the specifications passed in here.
322 For example, given branch_specs = ['factory', '>=R18'], we'd set things
323 up so that _FitsSpec() would return True for 'factory', or 'RXX'
Dan Shiaceb91d2013-02-20 12:41:28 -0800324 where XX is a number >= 18. Same check is done for branch_specs = [
325 'factory', '==R18'], which limit the test to only one specific branch.
Chris Masone96f16632012-04-04 18:36:03 -0700326
327 Given branch_specs = ['factory', 'firmware'], _FitsSpec()
328 would pass only those two specific strings.
329
330 Example usage:
Chris Masonecc4631d2012-04-20 12:06:39 -0700331 t = Task('Name', 'suite', ['factory', '>=R18'])
Chris Masone96f16632012-04-04 18:36:03 -0700332 t._FitsSpec('factory') # True
333 t._FitsSpec('R19') # True
334 t._FitsSpec('R17') # False
335 t._FitsSpec('firmware') # False
336 t._FitsSpec('goober') # False
337
Dan Shiaceb91d2013-02-20 12:41:28 -0800338 t = Task('Name', 'suite', ['factory', '==R18'])
339 t._FitsSpec('R19') # False, branch does not equal to 18
340 t._FitsSpec('R18') # True
341 t._FitsSpec('R17') # False
342
Dan Shia25e0d42015-07-23 15:00:04 -0700343 cros_build_spec and firmware_rw_build_spec are set for tests require
344 firmware update on the dut. Only one of them can be set.
345 For example:
346 branch_specs: ==tot
347 firmware_rw_build_spec: firmware
348 test_source: cros
349 This will run test using latest build on firmware branch, and the latest
350 ChromeOS build on ToT. The test source build is ChromeOS build.
351
352 branch_specs: firmware
353 cros_build_spec: ==tot-1
354 test_source: firmware_rw
355 This will run test using latest build on firmware branch, and the latest
356 ChromeOS build on dev channel (ToT-1). The test source build is the
357 firmware RW build.
358
Dan Shi59562a82016-01-06 16:05:31 -0800359 branch_specs: ==tot
360 firmware_rw_build_spec: cros
361 test_source: cros
362 This will run test using latest ChromeOS and firmware RW build on ToT.
363 ChromeOS build on ToT. The test source build is ChromeOS build.
364
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700365 @param name: name of this task, e.g. 'NightlyPower'
Chris Masonefad911a2012-03-29 12:30:26 -0700366 @param suite: the name of the suite to run, e.g. 'bvt'
Chris Masone96f16632012-04-04 18:36:03 -0700367 @param branch_specs: a pre-vetted iterable of branch specifiers,
368 e.g. ['>=R18', 'factory']
Chris Masonefad911a2012-03-29 12:30:26 -0700369 @param pool: the pool of machines to use for scheduling purposes.
370 Default: None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700371 @param num: the number of devices across which to shard the test suite.
Aviv Keshetd83ef442013-01-16 16:19:35 -0800372 Type: integer or None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700373 Default: None
Dan Shi9f256d92016-01-22 00:09:25 -0800374 @param boards: A comma separated list of boards to run this task on.
Alex Millerf2b57442013-09-07 18:40:02 -0700375 Default: Run on all boards.
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700376 @param priority: The string name of a priority from
377 client.common_lib.priorities.Priority.
378 @param timeout: The max lifetime of the suite in hours.
Prashanth B6de2bde2014-03-25 18:45:02 -0700379 @param file_bugs: True if bug filing is desired for the suite created
380 for this task.
Dan Shia25e0d42015-07-23 15:00:04 -0700381 @param cros_build_spec: Spec used to determine the ChromeOS build to
382 test with a firmware build, e.g., tot, R41 etc.
383 @param firmware_rw_build_spec: Spec used to determine the firmware build
384 test with a ChromeOS build.
385 @param test_source: The source of test code when firmware will be
386 updated in the test. The value can be `firmware_rw`
387 or `cros`.
Dan Shi29a16992015-09-22 11:29:58 -0700388 @param job_retry: Set to True to enable job-level retry. Default is
389 False.
Dan Shi9f256d92016-01-22 00:09:25 -0800390 @param hour: An integer specifying the hour that a nightly run should
391 be triggered, default is set to 21.
Dan Shice1f20a2016-01-25 17:27:40 -0800392 @param day: An integer specifying the day of a week that a weekly run
Dan Shi2121a332016-02-25 14:22:22 -0800393 should be triggered, default is set to 5, which is Saturday.
394 @param os_type: Type of OS, e.g., cros, brillo, android. Default is
395 cros. The argument is required for android/brillo builds.
396 @param launch_control_branches: Comma separated string of Launch Control
397 branches. The argument is required and only applicable for
398 android/brillo builds.
399 @param launch_control_targets: Comma separated string of build targets
400 for Launch Control builds. The argument is required and only
401 applicable for android/brillo builds.
Chris Masonefad911a2012-03-29 12:30:26 -0700402 """
Chris Masonecc4631d2012-04-20 12:06:39 -0700403 self._name = name
Chris Masonefad911a2012-03-29 12:30:26 -0700404 self._suite = suite
Chris Masone96f16632012-04-04 18:36:03 -0700405 self._branch_specs = branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700406 self._pool = pool
Aviv Keshetd83ef442013-01-16 16:19:35 -0800407 self._num = num
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700408 self._priority = priority
409 self._timeout = timeout
Prashanth B6de2bde2014-03-25 18:45:02 -0700410 self._file_bugs = file_bugs
Dan Shia25e0d42015-07-23 15:00:04 -0700411 self._cros_build_spec = cros_build_spec
412 self._firmware_rw_build_spec = firmware_rw_build_spec
413 self._test_source = test_source
Dan Shi29a16992015-09-22 11:29:58 -0700414 self._job_retry = job_retry
Dan Shi2121a332016-02-25 14:22:22 -0800415 self._hour = hour
416 self._day = day
417 self._os_type = os_type
418 self._launch_control_branches = (
419 [b.strip() for b in launch_control_branches.split(',')]
420 if launch_control_branches else [])
421 self._launch_control_targets = (
422 [t.strip() for t in launch_control_targets.split(',')]
423 if launch_control_targets else [])
Dan Shia25e0d42015-07-23 15:00:04 -0700424
425 if ((self._firmware_rw_build_spec or cros_build_spec) and
426 not self.test_source in [Builds.FIRMWARE_RW, Builds.CROS]):
427 raise MalformedConfigEntry(
428 'You must specify the build for test source. It can only '
429 'be `firmware_rw` or `cros`.')
430 if self._firmware_rw_build_spec and cros_build_spec:
431 raise MalformedConfigEntry(
432 'You cannot specify both firmware_rw_build_spec and '
433 'cros_build_spec. firmware_rw_build_spec is used to specify'
434 ' a firmware build when the suite requires firmware to be '
Dan Shi59562a82016-01-06 16:05:31 -0800435 'updated in the dut, its value can only be `firmware` or '
436 '`cros`. cros_build_spec is used to specify a ChromeOS '
437 'build when build_specs is set to firmware.')
Dan Shia25e0d42015-07-23 15:00:04 -0700438 if (self._firmware_rw_build_spec and
Dan Shi59562a82016-01-06 16:05:31 -0800439 self._firmware_rw_build_spec not in ['firmware', 'cros']):
Dan Shia25e0d42015-07-23 15:00:04 -0700440 raise MalformedConfigEntry(
Dan Shi59562a82016-01-06 16:05:31 -0800441 'firmware_rw_build_spec can only be empty, firmware or '
442 'cros. It does not support other build type yet.')
Chris Masone96f16632012-04-04 18:36:03 -0700443
444 self._bare_branches = []
Dan Shiaceb91d2013-02-20 12:41:28 -0800445 self._version_equal_constraint = False
Dan Shibde10772015-08-18 10:15:58 -0700446 self._version_gte_constraint = False
447 self._version_lte_constraint = False
Chris Masone67f06d62012-04-12 15:16:56 -0700448 if not branch_specs:
449 # Any milestone is OK.
450 self._numeric_constraint = version.LooseVersion('0')
451 else:
452 self._numeric_constraint = None
453 for spec in branch_specs:
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700454 if 'tot' in spec.lower():
455 tot_str = spec[spec.index('tot'):]
456 spec = spec.replace(
457 tot_str, TotMilestoneManager().ConvertTotSpec(
Dan Shibde10772015-08-18 10:15:58 -0700458 tot_str))
Chris Masone67f06d62012-04-12 15:16:56 -0700459 if spec.startswith('>='):
460 self._numeric_constraint = version.LooseVersion(
Dan Shibde10772015-08-18 10:15:58 -0700461 spec.lstrip('>=R'))
462 self._version_gte_constraint = True
463 elif spec.startswith('<='):
464 self._numeric_constraint = version.LooseVersion(
465 spec.lstrip('<=R'))
466 self._version_lte_constraint = True
Dan Shiaceb91d2013-02-20 12:41:28 -0800467 elif spec.startswith('=='):
468 self._version_equal_constraint = True
469 self._numeric_constraint = version.LooseVersion(
Dan Shibde10772015-08-18 10:15:58 -0700470 spec.lstrip('==R'))
Chris Masone67f06d62012-04-12 15:16:56 -0700471 else:
472 self._bare_branches.append(spec)
Alex Millerf2b57442013-09-07 18:40:02 -0700473
Aviv Keshet52c7a212015-12-07 15:27:22 -0800474 # Since we expect __hash__() and other comparator methods to be used
Chris Masonefad911a2012-03-29 12:30:26 -0700475 # frequently by set operations, and they use str() a lot, pre-compute
476 # the string representation of this object.
Aviv Keshet8ce3c982013-01-24 09:23:13 -0800477 if num is None:
478 numStr = '[Default num]'
479 else:
480 numStr = '%d' % num
Alex Millerf2b57442013-09-07 18:40:02 -0700481
482 if boards is None:
483 self._boards = set()
484 boardsStr = '[All boards]'
485 else:
486 self._boards = set([x.strip() for x in boards.split(',')])
487 boardsStr = boards
488
Dan Shi2121a332016-02-25 14:22:22 -0800489 if os_type == OS_TYPE_CROS:
490 self._str = ('%s: %s on %s with pool %s, boards [%s], file_bugs = '
491 '%s across %s machines.' %
492 (self.__class__.__name__, suite, branch_specs, pool,
493 boardsStr, self._file_bugs, numStr))
494 else:
495 self._str = ('%s: %s on branches %s and targets %s with pool %s, '
496 'boards [%s], file_bugs = %s across %s machines.' %
497 (self.__class__.__name__, suite,
498 launch_control_branches, launch_control_targets,
499 pool, boardsStr, self._file_bugs, numStr))
Chris Masone96f16632012-04-04 18:36:03 -0700500
501
502 def _FitsSpec(self, branch):
503 """Checks if a branch is deemed OK by this instance's branch specs.
504
505 When called on a branch name, will return whether that branch
Dan Shiaceb91d2013-02-20 12:41:28 -0800506 'fits' the specifications stored in self._bare_branches,
Dan Shibde10772015-08-18 10:15:58 -0700507 self._numeric_constraint, self._version_equal_constraint,
508 self._version_gte_constraint and self._version_lte_constraint.
Chris Masone96f16632012-04-04 18:36:03 -0700509
510 @param branch: the branch to check.
511 @return True if b 'fits' with stored specs, False otherwise.
512 """
Chris Masone657e1552012-05-30 17:06:20 -0700513 if branch in BARE_BRANCHES:
514 return branch in self._bare_branches
Dan Shiaceb91d2013-02-20 12:41:28 -0800515 if self._numeric_constraint:
516 if self._version_equal_constraint:
517 return version.LooseVersion(branch) == self._numeric_constraint
Dan Shibde10772015-08-18 10:15:58 -0700518 elif self._version_gte_constraint:
519 return version.LooseVersion(branch) >= self._numeric_constraint
520 elif self._version_lte_constraint:
521 return version.LooseVersion(branch) <= self._numeric_constraint
Dan Shiaceb91d2013-02-20 12:41:28 -0800522 else:
Dan Shibde10772015-08-18 10:15:58 -0700523 # Default to great or equal constraint.
Dan Shiaceb91d2013-02-20 12:41:28 -0800524 return version.LooseVersion(branch) >= self._numeric_constraint
525 else:
526 return False
Chris Masonefad911a2012-03-29 12:30:26 -0700527
528
529 @property
Alex Miller9979b5a2012-11-01 17:36:12 -0700530 def name(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800531 """Name of this task, e.g. 'NightlyPower'."""
Alex Miller9979b5a2012-11-01 17:36:12 -0700532 return self._name
533
534
535 @property
Chris Masonefad911a2012-03-29 12:30:26 -0700536 def suite(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800537 """Name of the suite to run, e.g. 'bvt'."""
Chris Masonefad911a2012-03-29 12:30:26 -0700538 return self._suite
539
540
541 @property
Chris Masone96f16632012-04-04 18:36:03 -0700542 def branch_specs(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800543 """a pre-vetted iterable of branch specifiers,
544 e.g. ['>=R18', 'factory']."""
Chris Masone96f16632012-04-04 18:36:03 -0700545 return self._branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700546
547
548 @property
Chris Masone3fba86f2012-04-03 10:06:56 -0700549 def pool(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800550 """The pool of machines to use for scheduling purposes."""
Chris Masone3fba86f2012-04-03 10:06:56 -0700551 return self._pool
Chris Masonefad911a2012-03-29 12:30:26 -0700552
553
Alex Miller9979b5a2012-11-01 17:36:12 -0700554 @property
555 def num(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800556 """The number of devices across which to shard the test suite.
557 Type: integer or None"""
Alex Miller9979b5a2012-11-01 17:36:12 -0700558 return self._num
559
560
Alex Millerf2b57442013-09-07 18:40:02 -0700561 @property
562 def boards(self):
563 """The boards on which to run this suite.
564 Type: Iterable of strings"""
565 return self._boards
566
567
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700568 @property
569 def priority(self):
570 """The priority of the suite"""
571 return self._priority
572
573
574 @property
575 def timeout(self):
576 """The maximum lifetime of the suite in hours."""
577 return self._timeout
578
579
Dan Shia25e0d42015-07-23 15:00:04 -0700580 @property
581 def cros_build_spec(self):
582 """The build spec of ChromeOS to test with a firmware build."""
583 return self._cros_build_spec
584
585
586 @property
587 def firmware_rw_build_spec(self):
588 """The build spec of firmware to test with a ChromeOS build."""
589 return self._firmware_rw_build_spec
590
591
592 @property
593 def test_source(self):
594 """Source of the test code, value can be `firmware_rw` or `cros`."""
595 return self._test_source
596
597
Dan Shi2121a332016-02-25 14:22:22 -0800598 @property
599 def hour(self):
600 """An integer specifying the hour that a nightly run should be triggered
601 """
602 return self._hour
603
604
605 @property
606 def day(self):
607 """An integer specifying the day of a week that a weekly run should be
608 triggered"""
609 return self._day
610
611
612 @property
613 def os_type(self):
614 """Type of OS, e.g., cros, brillo, android."""
615 return self._os_type
616
617
618 @property
619 def launch_control_branches(self):
620 """A list of Launch Control builds."""
621 return self._launch_control_branches
622
623
624 @property
625 def launch_control_targets(self):
626 """A list of Launch Control targets."""
627 return self._launch_control_targets
628
629
Chris Masonefad911a2012-03-29 12:30:26 -0700630 def __str__(self):
631 return self._str
632
633
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700634 def __repr__(self):
635 return self._str
636
637
Chris Masonefad911a2012-03-29 12:30:26 -0700638 def __lt__(self, other):
639 return str(self) < str(other)
640
641
642 def __le__(self, other):
643 return str(self) <= str(other)
644
645
646 def __eq__(self, other):
647 return str(self) == str(other)
648
649
650 def __ne__(self, other):
651 return str(self) != str(other)
652
653
654 def __gt__(self, other):
655 return str(self) > str(other)
656
657
658 def __ge__(self, other):
659 return str(self) >= str(other)
660
661
662 def __hash__(self):
663 """Allows instances to be correctly deduped when used in a set."""
664 return hash(str(self))
665
666
Dan Shia25e0d42015-07-23 15:00:04 -0700667 def _GetCrOSBuild(self, mv, board):
668 """Get the ChromeOS build name to test with firmware build.
669
670 The ChromeOS build to be used is determined by `self.cros_build_spec`.
671 Its value can be:
672 tot: use the latest ToT build.
673 tot-x: use the latest build in x milestone before ToT.
674 Rxx: use the latest build on xx milestone.
675
676 @param board: the board against which to run self._suite.
677 @param mv: an instance of manifest_versions.ManifestVersions.
678
679 @return: The ChromeOS build name to test with firmware build.
680
681 """
682 if not self.cros_build_spec:
683 return None
684 if self.cros_build_spec.startswith('tot'):
685 milestone = TotMilestoneManager().ConvertTotSpec(
686 self.cros_build_spec)[1:]
687 elif self.cros_build_spec.startswith('R'):
688 milestone = self.cros_build_spec[1:]
689 milestone, latest_manifest = mv.GetLatestManifest(
690 board, 'release', milestone=milestone)
691 latest_build = base_event.BuildName(board, 'release', milestone,
692 latest_manifest)
693 logging.debug('Found latest build of %s for spec %s: %s',
694 board, self.cros_build_spec, latest_build)
695 return latest_build
696
697
698 def _GetFirmwareRWBuild(self, mv, board, build_type):
699 """Get the firmware rw build name to test with ChromeOS build.
700
701 The firmware rw build to be used is determined by
Dan Shi59562a82016-01-06 16:05:31 -0800702 `self.firmware_rw_build_spec`. Its value can be `firmware`, `cros` or
703 empty:
Dan Shia25e0d42015-07-23 15:00:04 -0700704 firmware: use the ToT build in firmware branch.
Dan Shi59562a82016-01-06 16:05:31 -0800705 cros: use the ToT build in release (ChromeOS) branch.
Dan Shia25e0d42015-07-23 15:00:04 -0700706
707 @param mv: an instance of manifest_versions.ManifestVersions.
708 @param board: the board against which to run self._suite.
Dan Shi59562a82016-01-06 16:05:31 -0800709 @param build_type: Build type of the firmware build, e.g., factory,
710 firmware or release.
Dan Shia25e0d42015-07-23 15:00:04 -0700711
712 @return: The firmware rw build name to test with ChromeOS build.
713
714 """
715 if not self.firmware_rw_build_spec:
716 return None
717 latest_milestone, latest_manifest = mv.GetLatestManifest(
718 board, build_type)
719 latest_build = base_event.BuildName(board, build_type, latest_milestone,
720 latest_manifest)
721 logging.debug('Found latest firmware build of %s for spec %s: %s',
722 board, self.firmware_rw_build_spec, latest_build)
723 return latest_build
724
725
Alex Miller7ce1b362012-07-10 09:24:10 -0700726 def AvailableHosts(self, scheduler, board):
727 """Query what hosts are able to run a test on a board and pool
728 combination.
Alex Miller511a9e32012-07-03 09:16:47 -0700729
730 @param scheduler: an instance of DedupingScheduler, as defined in
731 deduping_scheduler.py
732 @param board: the board against which one wants to run the test.
Alex Miller7ce1b362012-07-10 09:24:10 -0700733 @return The list of hosts meeting the board and pool requirements,
734 or None if no hosts were found."""
Alex Millerf2b57442013-09-07 18:40:02 -0700735 if self._boards and board not in self._boards:
736 return []
737
Alex Miller511a9e32012-07-03 09:16:47 -0700738 labels = [Labels.BOARD_PREFIX + board]
739 if self._pool:
Chris Masonecd214e02012-07-10 16:22:10 -0700740 labels.append(Labels.POOL_PREFIX + self._pool)
Alex Miller511a9e32012-07-03 09:16:47 -0700741
Prashanth B6de2bde2014-03-25 18:45:02 -0700742 return scheduler.CheckHostsExist(multiple_labels=labels)
Alex Miller511a9e32012-07-03 09:16:47 -0700743
744
Alex Millerd621cf22012-07-11 13:57:10 -0700745 def ShouldHaveAvailableHosts(self):
746 """As a sanity check, return true if we know for certain that
747 we should be able to schedule this test. If we claim this test
748 should be able to run, and it ends up not being scheduled, then
749 a warning will be reported.
750
751 @return True if this test should be able to run, False otherwise.
752 """
753 return self._pool == 'bvt'
754
755
Dan Shi2121a332016-02-25 14:22:22 -0800756 def _ScheduleSuite(self, scheduler, cros_build, firmware_rw_build,
757 test_source_build, launch_control_build, board, force,
758 run_prod_code=False):
759 """Try to schedule a suite with given build and board information.
760
761 @param scheduler: an instance of DedupingScheduler, as defined in
762 deduping_scheduler.py
763 @oaran build: Build to run suite for, e.g., 'daisy-release/R18-1655.0.0'
764 and 'git_mnc_release/shamu-eng/123'.
765 @param firmware_rw_build: Firmware RW build to run test with.
766 @param test_source_build: Test source build, used for server-side
767 packaging.
768 @param launch_control_build: Name of a Launch Control build, e.g.,
769 'git_mnc_release/shamu-eng/123'
770 @param board: the board against which to run self._suite.
771 @param force: Always schedule the suite.
772 @param run_prod_code: If True, the suite will run the test code that
773 lives in prod aka the test code currently on the
774 lab servers. If False, the control files and test
775 code for this suite run will be retrieved from the
776 build artifacts. Default is False.
777 """
778 test_source_build_msg = (
779 ' Test source build is %s.' % test_source_build
780 if test_source_build else None)
781 firmware_rw_build_msg = (
782 ' Firmware RW build is %s.' % firmware_rw_build
783 if firmware_rw_build else None)
784 build_string = cros_build or launch_control_build
785 logging.debug('Schedule %s for build %s.%s%s',
786 self._suite, build_string, test_source_build_msg,
787 firmware_rw_build_msg)
788
789 if not scheduler.ScheduleSuite(
790 self._suite, board, cros_build, self._pool, self._num,
791 self._priority, self._timeout, force,
792 file_bugs=self._file_bugs,
793 firmware_rw_build=firmware_rw_build,
794 test_source_build=test_source_build,
795 job_retry=self._job_retry,
796 launch_control_build=launch_control_build,
797 run_prod_code=run_prod_code):
798 logging.info('Skipping scheduling %s on %s for %s',
799 self._suite, build_string, board)
800
801
802 def _Run_CrOS_Builds(self, scheduler, branch_builds, board, force=False,
803 mv=None):
804 """Run this task for CrOS builds. Returns False if it should be
805 destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -0700806
Chris Masone013859b2012-04-01 13:45:26 -0700807 Execute this task. Attempt to schedule the associated suite.
808 Return True if this task should be kept around, False if it
809 should be destroyed. This allows for one-shot Tasks.
Chris Masonefad911a2012-03-29 12:30:26 -0700810
811 @param scheduler: an instance of DedupingScheduler, as defined in
812 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -0700813 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -0700814 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -0700815 {'R18': ['x86-alex-release/R18-1655.0.0'],
816 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -0700817 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -0700818 @param force: Always schedule the suite.
Dan Shia25e0d42015-07-23 15:00:04 -0700819 @param mv: an instance of manifest_versions.ManifestVersions.
820
Chris Masone013859b2012-04-01 13:45:26 -0700821 @return True if the task should be kept, False if not
Dan Shia25e0d42015-07-23 15:00:04 -0700822
Chris Masonefad911a2012-03-29 12:30:26 -0700823 """
Chris Masonea3a38172012-05-14 15:19:56 -0700824 logging.info('Running %s on %s', self._name, board)
Dan Shia25e0d42015-07-23 15:00:04 -0700825 is_firmware_build = 'firmware' in self.branch_specs
826 # firmware_rw_build is only needed if firmware_rw_build_spec is given.
827 firmware_rw_build = None
828 try:
829 if is_firmware_build:
830 # When build specified in branch_specs is a firmware build,
831 # we need a ChromeOS build to test with the firmware build.
832 cros_build = self._GetCrOSBuild(mv, board)
833 elif self.firmware_rw_build_spec:
834 # When firmware_rw_build_spec is specified, the test involves
835 # updating the firmware by firmware build specified in
836 # firmware_rw_build_spec.
Dan Shi59562a82016-01-06 16:05:31 -0800837 if self.firmware_rw_build_spec == 'cros':
838 build_type = 'release'
839 else:
840 build_type = self.firmware_rw_build_spec
Dan Shia25e0d42015-07-23 15:00:04 -0700841 firmware_rw_build = self._GetFirmwareRWBuild(
Dan Shi59562a82016-01-06 16:05:31 -0800842 mv, board, build_type)
Dan Shia25e0d42015-07-23 15:00:04 -0700843 except manifest_versions.QueryException as e:
844 logging.error(e)
845 logging.error('Running %s on %s is failed. Failed to find build '
846 'required to run the suite.', self._name, board)
847 return False
848
Chris Masone96f16632012-04-04 18:36:03 -0700849 builds = []
Chris Masonefe5a5092012-04-11 18:29:07 -0700850 for branch, build in branch_builds.iteritems():
Chris Masonea3a38172012-05-14 15:19:56 -0700851 logging.info('Checking if %s fits spec %r',
852 branch, self.branch_specs)
Chris Masone96f16632012-04-04 18:36:03 -0700853 if self._FitsSpec(branch):
Dan Shibde10772015-08-18 10:15:58 -0700854 logging.debug('Build %s fits the spec.', build)
Chris Masone05b19442012-04-17 13:37:55 -0700855 builds.extend(build)
Chris Masone96f16632012-04-04 18:36:03 -0700856 for build in builds:
Chris Masone3fba86f2012-04-03 10:06:56 -0700857 try:
Dan Shia25e0d42015-07-23 15:00:04 -0700858 if is_firmware_build:
859 firmware_rw_build = build
860 else:
861 cros_build = build
862 if self.test_source == Builds.FIRMWARE_RW:
863 test_source_build = firmware_rw_build
864 elif self.test_source == Builds.CROS:
865 test_source_build = cros_build
866 else:
867 test_source_build = None
Dan Shi2121a332016-02-25 14:22:22 -0800868 self._ScheduleSuite(scheduler, cros_build, firmware_rw_build,
869 test_source_build, None, board, force)
Chris Masone3fba86f2012-04-03 10:06:56 -0700870 except deduping_scheduler.DedupingSchedulerException as e:
871 logging.error(e)
Chris Masonefad911a2012-03-29 12:30:26 -0700872 return True
873
874
Dan Shi2121a332016-02-25 14:22:22 -0800875 def _Run_LaunchControl_Builds(self, scheduler, launch_control_builds, board,
876 force=False):
877 """Run this task. Returns False if it should be destroyed.
878
879 Execute this task. Attempt to schedule the associated suite.
880 Return True if this task should be kept around, False if it
881 should be destroyed. This allows for one-shot Tasks.
882
883 @param scheduler: an instance of DedupingScheduler, as defined in
884 deduping_scheduler.py
885 @param launch_control_builds: A list of Launch Control builds.
886 @param board: the board against which to run self._suite.
887 @param force: Always schedule the suite.
888
889 @return True if the task should be kept, False if not
890
891 """
892 logging.info('Running %s on %s', self._name, board)
893 for build in launch_control_builds:
894 try:
895 self._ScheduleSuite(scheduler, None, None, None,
896 launch_control_build=build, board=board,
897 force=force, run_prod_code=True)
898 except deduping_scheduler.DedupingSchedulerException as e:
899 logging.error(e)
900 return True
901
902
903 def Run(self, scheduler, branch_builds, board, force=False, mv=None,
904 launch_control_builds=None):
905 """Run this task. Returns False if it should be destroyed.
906
907 Execute this task. Attempt to schedule the associated suite.
908 Return True if this task should be kept around, False if it
909 should be destroyed. This allows for one-shot Tasks.
910
911 @param scheduler: an instance of DedupingScheduler, as defined in
912 deduping_scheduler.py
913 @param branch_builds: a dict mapping branch name to the build(s) to
914 install for that branch, e.g.
915 {'R18': ['x86-alex-release/R18-1655.0.0'],
916 'R19': ['x86-alex-release/R19-2077.0.0']}
917 @param board: the board against which to run self._suite.
918 @param force: Always schedule the suite.
919 @param mv: an instance of manifest_versions.ManifestVersions.
920 @param launch_control_builds: A list of Launch Control builds.
921
922 @return True if the task should be kept, False if not
923
924 """
925 if ((self._os_type == OS_TYPE_CROS and not branch_builds) or
926 (self._os_type != OS_TYPE_CROS and not launch_control_builds)):
927 logging.debug('No build to run, skip running %s on %s.', self._name,
928 board)
929 # Return True so the task will be kept, as the given build and board
930 # do not match.
931 return True
932
933 if self._os_type == OS_TYPE_CROS:
934 return self._Run_CrOS_Builds(
935 scheduler, branch_builds, board, force, mv)
936 else:
937 return self._Run_LaunchControl_Builds(
938 scheduler, launch_control_builds, board, force)
939
940
Chris Masone013859b2012-04-01 13:45:26 -0700941class OneShotTask(Task):
942 """A Task that can be run only once. Can schedule itself."""
Chris Masonefad911a2012-03-29 12:30:26 -0700943
944
Dan Shi2121a332016-02-25 14:22:22 -0800945 def Run(self, scheduler, branch_builds, board, force=False, mv=None,
946 launch_control_builds=None):
Chris Masone013859b2012-04-01 13:45:26 -0700947 """Run this task. Returns False, indicating it should be destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -0700948
Chris Masone013859b2012-04-01 13:45:26 -0700949 Run this task. Attempt to schedule the associated suite.
950 Return False, indicating to the caller that it should discard this task.
Chris Masonefad911a2012-03-29 12:30:26 -0700951
952 @param scheduler: an instance of DedupingScheduler, as defined in
953 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -0700954 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -0700955 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -0700956 {'R18': ['x86-alex-release/R18-1655.0.0'],
957 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -0700958 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -0700959 @param force: Always schedule the suite.
Dan Shia25e0d42015-07-23 15:00:04 -0700960 @param mv: an instance of manifest_versions.ManifestVersions.
Dan Shi2121a332016-02-25 14:22:22 -0800961 @param launch_control_builds: A list of Launch Control builds.
Dan Shia25e0d42015-07-23 15:00:04 -0700962
Chris Masonefad911a2012-03-29 12:30:26 -0700963 @return False
Dan Shia25e0d42015-07-23 15:00:04 -0700964
Chris Masonefad911a2012-03-29 12:30:26 -0700965 """
Dan Shia25e0d42015-07-23 15:00:04 -0700966 super(OneShotTask, self).Run(scheduler, branch_builds, board, force,
Dan Shi2121a332016-02-25 14:22:22 -0800967 mv, launch_control_builds)
Chris Masonefad911a2012-03-29 12:30:26 -0700968 return False