blob: f786f13909b760223655627db52e94e8436585ad [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
Dan Shi481b8b62016-03-08 13:03:08 -080028_WEEKDAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday',
29 'Sunday']
30
Chris Masone96f16632012-04-04 18:36:03 -070031class MalformedConfigEntry(Exception):
32 """Raised to indicate a failure to parse a Task out of a config."""
33 pass
Chris Masonefad911a2012-03-29 12:30:26 -070034
35
Alex Miller0d003572013-03-18 11:51:30 -070036BARE_BRANCHES = ['factory', 'firmware']
Chris Masone67f06d62012-04-12 15:16:56 -070037
38
39def PickBranchName(type, milestone):
Dan Shiaceb91d2013-02-20 12:41:28 -080040 """Pick branch name. If type is among BARE_BRANCHES, return type,
41 otherwise, return milestone.
42
43 @param type: type of the branch, e.g., 'release', 'factory', or 'firmware'
44 @param milestone: CrOS milestone number
45 """
Chris Masone67f06d62012-04-12 15:16:56 -070046 if type in BARE_BRANCHES:
47 return type
48 return milestone
49
50
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070051class TotMilestoneManager(object):
52 """A class capable of converting tot string to milestone numbers.
53
54 This class is used as a cache for the tot milestone, so we don't
55 repeatedly hit google storage for all O(100) tasks in suite
56 scheduler's ini file.
57 """
58
Fang Dengf08814a2015-08-03 18:12:18 +000059 __metaclass__ = server_utils.Singleton
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070060
Dan Shi23245142015-01-22 13:22:28 -080061 # True if suite_scheduler is running for sanity check. When it's set to
62 # True, the code won't make gsutil call to get the actual tot milestone to
63 # avoid dependency on the installation of gsutil to run sanity check.
64 is_sanity = False
65
66
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070067 @staticmethod
68 def _tot_milestone():
69 """Get the tot milestone, eg: R40
70
71 @returns: A string representing the Tot milestone as declared by
72 the LATEST_BUILD_URL, or an empty string if LATEST_BUILD_URL
73 doesn't exist.
74 """
Dan Shi23245142015-01-22 13:22:28 -080075 if TotMilestoneManager.is_sanity:
76 logging.info('suite_scheduler is running for sanity purpose, no '
77 'need to get the actual tot milestone string.')
78 return 'R40'
79
Prashanth Balasubramanian1b859622014-10-28 16:02:15 -070080 cmd = ['gsutil', 'cat', constants.LATEST_BUILD_URL]
81 proc = subprocess.Popen(
82 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
83 stdout, stderr = proc.communicate()
84 if proc.poll():
85 logging.warning('Failed to get latest build: %s', stderr)
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070086 return ''
Prashanth Balasubramanian1b859622014-10-28 16:02:15 -070087 return stdout.split('-')[0]
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070088
89
90 def refresh(self):
91 """Refresh the tot milestone string managed by this class."""
92 self.tot = self._tot_milestone()
93
94
95 def __init__(self):
96 """Initialize a TotMilestoneManager."""
97 self.refresh()
98
99
100 def ConvertTotSpec(self, tot_spec):
101 """Converts a tot spec to the appropriate milestone.
102
103 Assume tot is R40:
104 tot -> R40
105 tot-1 -> R39
106 tot-2 -> R38
107 tot-(any other numbers) -> R40
108
109 With the last option one assumes that a malformed configuration that has
110 'tot' in it, wants at least tot.
111
112 @param tot_spec: A string representing the tot spec.
113 @raises MalformedConfigEntry: If the tot_spec doesn't match the
114 expected format.
115 """
116 tot_spec = tot_spec.lower()
117 match = re.match('(tot)[-]?(1$|2$)?', tot_spec)
118 if not match:
119 raise MalformedConfigEntry(
120 "%s isn't a valid branch spec." % tot_spec)
121 tot_mstone = self.tot
122 num_back = match.groups()[1]
123 if num_back:
124 tot_mstone_num = tot_mstone.lstrip('R')
125 tot_mstone = tot_mstone.replace(
126 tot_mstone_num, str(int(tot_mstone_num)-int(num_back)))
127 return tot_mstone
128
129
Chris Masone013859b2012-04-01 13:45:26 -0700130class Task(object):
Chris Masonefad911a2012-03-29 12:30:26 -0700131 """Represents an entry from the scheduler config. Can schedule itself.
132
133 Each entry from the scheduler config file maps one-to-one to a
Chris Masone013859b2012-04-01 13:45:26 -0700134 Task. Each instance has enough info to schedule itself
Chris Masonefad911a2012-03-29 12:30:26 -0700135 on-demand with the AFE.
136
Aviv Keshet52c7a212015-12-07 15:27:22 -0800137 This class also overrides __hash__() and all comparator methods to enable
Chris Masonefad911a2012-03-29 12:30:26 -0700138 correct use in dicts, sets, etc.
139 """
140
Chris Masone96f16632012-04-04 18:36:03 -0700141
142 @staticmethod
143 def CreateFromConfigSection(config, section):
144 """Create a Task from a section of a config file.
145
146 The section to parse should look like this:
147 [TaskName]
148 suite: suite_to_run # Required
149 run_on: event_on which to run # Required
Dan Shi9f256d92016-01-22 00:09:25 -0800150 hour: integer of the hour to run, only applies to nightly. # Optional
Dan Shiaceb91d2013-02-20 12:41:28 -0800151 branch_specs: factory,firmware,>=R12 or ==R12 # Optional
Chris Masone96f16632012-04-04 18:36:03 -0700152 pool: pool_of_devices # Optional
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700153 num: sharding_factor # int, Optional
Alex Millerf2b57442013-09-07 18:40:02 -0700154 boards: board1, board2 # comma seperated string, Optional
Dan Shi2121a332016-02-25 14:22:22 -0800155 # Settings for Launch Control builds only:
156 os_type: brillo # Type of OS, e.g., cros, brillo, android. Default is
157 cros. Required for android/brillo builds.
158 branches: git_mnc_release # comma separated string of Launch Control
159 branches. Required and only applicable for android/brillo
160 builds.
161 targets: dragonboard-eng # comma separated string of build targets.
162 Required and only applicable for android/brillo builds.
Chris Masone96f16632012-04-04 18:36:03 -0700163
Chris Masone67f06d62012-04-12 15:16:56 -0700164 By default, Tasks run on all release branches, not factory or firmware.
165
Chris Masone96f16632012-04-04 18:36:03 -0700166 @param config: a ForgivingConfigParser.
167 @param section: the section to parse into a Task.
168 @return keyword, Task object pair. One or both will be None on error.
169 @raise MalformedConfigEntry if there's a problem parsing |section|.
170 """
Alex Miller06695022012-07-18 09:31:36 -0700171 if not config.has_section(section):
172 raise MalformedConfigEntry('unknown section %s' % section)
173
Alex Millerf2b57442013-09-07 18:40:02 -0700174 allowed = set(['suite', 'run_on', 'branch_specs', 'pool', 'num',
Dan Shia25e0d42015-07-23 15:00:04 -0700175 'boards', 'file_bugs', 'cros_build_spec',
Dan Shib49bb8b2016-03-01 15:29:27 -0800176 'firmware_rw_build_spec', 'firmware_ro_build_spec',
177 'test_source', 'job_retry', 'hour', 'day', 'branches',
178 'targets', 'os_type'])
Alex Millerbb535e22012-07-11 20:11:33 -0700179 # The parameter of union() is the keys under the section in the config
180 # The union merges this with the allowed set, so if any optional keys
181 # are omitted, then they're filled in. If any extra keys are present,
182 # then they will expand unioned set, causing it to fail the following
183 # comparison against the allowed set.
184 section_headers = allowed.union(dict(config.items(section)).keys())
185 if allowed != section_headers:
186 raise MalformedConfigEntry('unknown entries: %s' %
187 ", ".join(map(str, section_headers.difference(allowed))))
188
Chris Masone96f16632012-04-04 18:36:03 -0700189 keyword = config.getstring(section, 'run_on')
Dan Shi9f256d92016-01-22 00:09:25 -0800190 hour = config.getstring(section, 'hour')
Chris Masone96f16632012-04-04 18:36:03 -0700191 suite = config.getstring(section, 'suite')
Dan Shi2121a332016-02-25 14:22:22 -0800192 branch_specs = config.getstring(section, 'branch_specs')
Chris Masone96f16632012-04-04 18:36:03 -0700193 pool = config.getstring(section, 'pool')
Alex Millerf2b57442013-09-07 18:40:02 -0700194 boards = config.getstring(section, 'boards')
Prashanth B6de2bde2014-03-25 18:45:02 -0700195 file_bugs = config.getboolean(section, 'file_bugs')
Dan Shia25e0d42015-07-23 15:00:04 -0700196 cros_build_spec = config.getstring(section, 'cros_build_spec')
197 firmware_rw_build_spec = config.getstring(
198 section, 'firmware_rw_build_spec')
Dan Shib49bb8b2016-03-01 15:29:27 -0800199 firmware_ro_build_spec = config.getstring(
200 section, 'firmware_ro_build_spec')
Dan Shia25e0d42015-07-23 15:00:04 -0700201 test_source = config.getstring(section, 'test_source')
Dan Shi29a16992015-09-22 11:29:58 -0700202 job_retry = config.getboolean(section, 'job_retry')
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700203 for klass in driver.Driver.EVENT_CLASSES:
204 if klass.KEYWORD == keyword:
205 priority = klass.PRIORITY
206 timeout = klass.TIMEOUT
207 break
208 else:
209 priority = None
210 timeout = None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700211 try:
212 num = config.getint(section, 'num')
213 except ValueError as e:
Dan Shi9f256d92016-01-22 00:09:25 -0800214 raise MalformedConfigEntry("Ill-specified 'num': %r" % e)
Chris Masone96f16632012-04-04 18:36:03 -0700215 if not keyword:
216 raise MalformedConfigEntry('No event to |run_on|.')
217 if not suite:
218 raise MalformedConfigEntry('No |suite|')
Dan Shi9f256d92016-01-22 00:09:25 -0800219 try:
220 hour = config.getint(section, 'hour')
221 except ValueError as e:
222 raise MalformedConfigEntry("Ill-specified 'hour': %r" % e)
223 if hour is not None and (hour < 0 or hour > 23):
224 raise MalformedConfigEntry(
225 '`hour` must be an integer between 0 and 23.')
226 if hour is not None and keyword != 'nightly':
227 raise MalformedConfigEntry(
228 '`hour` is the trigger time that can only apply to nightly '
229 'event.')
Dan Shice1f20a2016-01-25 17:27:40 -0800230
231 try:
232 day = config.getint(section, 'day')
233 except ValueError as e:
234 raise MalformedConfigEntry("Ill-specified 'day': %r" % e)
235 if day is not None and (day < 0 or day > 6):
236 raise MalformedConfigEntry(
237 '`day` must be an integer between 0 and 6, where 0 is for '
238 'Monday and 6 is for Sunday.')
239 if day is not None and keyword != 'weekly':
240 raise MalformedConfigEntry(
241 '`day` is the trigger of the day of a week, that can only '
242 'apply to weekly events.')
243
Chris Masone96f16632012-04-04 18:36:03 -0700244 specs = []
Dan Shi2121a332016-02-25 14:22:22 -0800245 if branch_specs:
246 specs = re.split('\s*,\s*', branch_specs)
Chris Masone96f16632012-04-04 18:36:03 -0700247 Task.CheckBranchSpecs(specs)
Dan Shi2121a332016-02-25 14:22:22 -0800248
249 os_type = config.getstring(section, 'os_type') or OS_TYPE_CROS
250 if os_type not in OS_TYPES:
251 raise MalformedConfigEntry('`os_type` must be one of %s' % OS_TYPES)
252
253 lc_branches = config.getstring(section, 'branches')
254 lc_targets = config.getstring(section, 'targets')
255 if os_type == OS_TYPE_CROS and (lc_branches or lc_targets):
256 raise MalformedConfigEntry(
257 '`branches` and `targets` are only supported for Launch '
258 'Control builds, not ChromeOS builds.')
259 if (os_type in OS_TYPES_LAUNCH_CONTROL and
260 (not lc_branches or not lc_targets)):
261 raise MalformedConfigEntry(
262 '`branches` and `targets` must be specified for Launch '
263 'Control builds.')
264 if os_type in OS_TYPES_LAUNCH_CONTROL and boards:
265 raise MalformedConfigEntry(
266 '`boards` for Launch Control builds are retrieved from '
267 '`targets` setting, it should not be set for Launch '
268 'Control builds.')
269
270 # Extract boards from targets list.
271 if os_type in OS_TYPES_LAUNCH_CONTROL:
272 boards = ''
273 for target in lc_targets.split(','):
274 board_name, _ = server_utils.parse_launch_control_target(
275 target.strip())
276 boards += '%s-%s,' % (os_type, board_name)
277 boards = boards.strip(',')
278
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700279 return keyword, Task(section, suite, specs, pool, num, boards,
Prashanth B6de2bde2014-03-25 18:45:02 -0700280 priority, timeout,
Dan Shia25e0d42015-07-23 15:00:04 -0700281 file_bugs=file_bugs if file_bugs else False,
282 cros_build_spec=cros_build_spec,
283 firmware_rw_build_spec=firmware_rw_build_spec,
Dan Shib49bb8b2016-03-01 15:29:27 -0800284 firmware_ro_build_spec=firmware_ro_build_spec,
Dan Shi9f256d92016-01-22 00:09:25 -0800285 test_source=test_source, job_retry=job_retry,
Dan Shi2121a332016-02-25 14:22:22 -0800286 hour=hour, day=day, os_type=os_type,
287 launch_control_branches=lc_branches,
288 launch_control_targets=lc_targets)
Chris Masone96f16632012-04-04 18:36:03 -0700289
290
291 @staticmethod
292 def CheckBranchSpecs(branch_specs):
293 """Make sure entries in the list branch_specs are correctly formed.
294
Chris Masone67f06d62012-04-12 15:16:56 -0700295 We accept any of BARE_BRANCHES in |branch_specs|, as
Dan Shiaceb91d2013-02-20 12:41:28 -0800296 well as _one_ string of the form '>=RXX' or '==RXX', where 'RXX' is a
Chris Masone96f16632012-04-04 18:36:03 -0700297 CrOS milestone number.
298
299 @param branch_specs: an iterable of branch specifiers.
300 @raise MalformedConfigEntry if there's a problem parsing |branch_specs|.
301 """
302 have_seen_numeric_constraint = False
303 for branch in branch_specs:
Chris Masone67f06d62012-04-12 15:16:56 -0700304 if branch in BARE_BRANCHES:
Chris Masone96f16632012-04-04 18:36:03 -0700305 continue
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700306 if not have_seen_numeric_constraint:
307 #TODO(beeps): Why was <= dropped on the floor?
308 if branch.startswith('>=R') or branch.startswith('==R'):
309 have_seen_numeric_constraint = True
310 elif 'tot' in branch:
311 TotMilestoneManager().ConvertTotSpec(
312 branch[branch.index('tot'):])
313 have_seen_numeric_constraint = True
Chris Masone96f16632012-04-04 18:36:03 -0700314 continue
Chris Masone97cf0a72012-05-16 09:55:52 -0700315 raise MalformedConfigEntry("%s isn't a valid branch spec." % branch)
Chris Masone96f16632012-04-04 18:36:03 -0700316
317
Alex Millerf2b57442013-09-07 18:40:02 -0700318 def __init__(self, name, suite, branch_specs, pool=None, num=None,
Dan Shia25e0d42015-07-23 15:00:04 -0700319 boards=None, priority=None, timeout=None, file_bugs=False,
320 cros_build_spec=None, firmware_rw_build_spec=None,
Dan Shib49bb8b2016-03-01 15:29:27 -0800321 firmware_ro_build_spec=None, test_source=None, job_retry=False,
322 hour=None, day=None, os_type=OS_TYPE_CROS,
323 launch_control_branches=None, launch_control_targets=None):
Chris Masonefad911a2012-03-29 12:30:26 -0700324 """Constructor
325
Chris Masone96f16632012-04-04 18:36:03 -0700326 Given an iterable in |branch_specs|, pre-vetted using CheckBranchSpecs,
327 we'll store them such that _FitsSpec() can be used to check whether a
328 given branch 'fits' with the specifications passed in here.
329 For example, given branch_specs = ['factory', '>=R18'], we'd set things
330 up so that _FitsSpec() would return True for 'factory', or 'RXX'
Dan Shiaceb91d2013-02-20 12:41:28 -0800331 where XX is a number >= 18. Same check is done for branch_specs = [
332 'factory', '==R18'], which limit the test to only one specific branch.
Chris Masone96f16632012-04-04 18:36:03 -0700333
334 Given branch_specs = ['factory', 'firmware'], _FitsSpec()
335 would pass only those two specific strings.
336
337 Example usage:
Chris Masonecc4631d2012-04-20 12:06:39 -0700338 t = Task('Name', 'suite', ['factory', '>=R18'])
Chris Masone96f16632012-04-04 18:36:03 -0700339 t._FitsSpec('factory') # True
340 t._FitsSpec('R19') # True
341 t._FitsSpec('R17') # False
342 t._FitsSpec('firmware') # False
343 t._FitsSpec('goober') # False
344
Dan Shiaceb91d2013-02-20 12:41:28 -0800345 t = Task('Name', 'suite', ['factory', '==R18'])
346 t._FitsSpec('R19') # False, branch does not equal to 18
347 t._FitsSpec('R18') # True
348 t._FitsSpec('R17') # False
349
Dan Shia25e0d42015-07-23 15:00:04 -0700350 cros_build_spec and firmware_rw_build_spec are set for tests require
351 firmware update on the dut. Only one of them can be set.
352 For example:
353 branch_specs: ==tot
354 firmware_rw_build_spec: firmware
355 test_source: cros
356 This will run test using latest build on firmware branch, and the latest
357 ChromeOS build on ToT. The test source build is ChromeOS build.
358
359 branch_specs: firmware
360 cros_build_spec: ==tot-1
361 test_source: firmware_rw
362 This will run test using latest build on firmware branch, and the latest
363 ChromeOS build on dev channel (ToT-1). The test source build is the
364 firmware RW build.
365
Dan Shi59562a82016-01-06 16:05:31 -0800366 branch_specs: ==tot
367 firmware_rw_build_spec: cros
368 test_source: cros
369 This will run test using latest ChromeOS and firmware RW build on ToT.
370 ChromeOS build on ToT. The test source build is ChromeOS build.
371
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700372 @param name: name of this task, e.g. 'NightlyPower'
Chris Masonefad911a2012-03-29 12:30:26 -0700373 @param suite: the name of the suite to run, e.g. 'bvt'
Chris Masone96f16632012-04-04 18:36:03 -0700374 @param branch_specs: a pre-vetted iterable of branch specifiers,
375 e.g. ['>=R18', 'factory']
Chris Masonefad911a2012-03-29 12:30:26 -0700376 @param pool: the pool of machines to use for scheduling purposes.
377 Default: None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700378 @param num: the number of devices across which to shard the test suite.
Aviv Keshetd83ef442013-01-16 16:19:35 -0800379 Type: integer or None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700380 Default: None
Dan Shi9f256d92016-01-22 00:09:25 -0800381 @param boards: A comma separated list of boards to run this task on.
Alex Millerf2b57442013-09-07 18:40:02 -0700382 Default: Run on all boards.
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700383 @param priority: The string name of a priority from
384 client.common_lib.priorities.Priority.
385 @param timeout: The max lifetime of the suite in hours.
Prashanth B6de2bde2014-03-25 18:45:02 -0700386 @param file_bugs: True if bug filing is desired for the suite created
387 for this task.
Dan Shia25e0d42015-07-23 15:00:04 -0700388 @param cros_build_spec: Spec used to determine the ChromeOS build to
389 test with a firmware build, e.g., tot, R41 etc.
Dan Shib49bb8b2016-03-01 15:29:27 -0800390 @param firmware_rw_build_spec: Spec used to determine the firmware RW
391 build test with a ChromeOS build.
392 @param firmware_ro_build_spec: Spec used to determine the firmware RO
393 build test with a ChromeOS build.
Dan Shia25e0d42015-07-23 15:00:04 -0700394 @param test_source: The source of test code when firmware will be
Dan Shib49bb8b2016-03-01 15:29:27 -0800395 updated in the test. The value can be `firmware_rw`,
396 `firmware_ro` or `cros`.
Dan Shi29a16992015-09-22 11:29:58 -0700397 @param job_retry: Set to True to enable job-level retry. Default is
398 False.
Dan Shi9f256d92016-01-22 00:09:25 -0800399 @param hour: An integer specifying the hour that a nightly run should
400 be triggered, default is set to 21.
Dan Shice1f20a2016-01-25 17:27:40 -0800401 @param day: An integer specifying the day of a week that a weekly run
Dan Shi2121a332016-02-25 14:22:22 -0800402 should be triggered, default is set to 5, which is Saturday.
403 @param os_type: Type of OS, e.g., cros, brillo, android. Default is
404 cros. The argument is required for android/brillo builds.
405 @param launch_control_branches: Comma separated string of Launch Control
406 branches. The argument is required and only applicable for
407 android/brillo builds.
408 @param launch_control_targets: Comma separated string of build targets
409 for Launch Control builds. The argument is required and only
410 applicable for android/brillo builds.
Chris Masonefad911a2012-03-29 12:30:26 -0700411 """
Chris Masonecc4631d2012-04-20 12:06:39 -0700412 self._name = name
Chris Masonefad911a2012-03-29 12:30:26 -0700413 self._suite = suite
Chris Masone96f16632012-04-04 18:36:03 -0700414 self._branch_specs = branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700415 self._pool = pool
Aviv Keshetd83ef442013-01-16 16:19:35 -0800416 self._num = num
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700417 self._priority = priority
418 self._timeout = timeout
Prashanth B6de2bde2014-03-25 18:45:02 -0700419 self._file_bugs = file_bugs
Dan Shia25e0d42015-07-23 15:00:04 -0700420 self._cros_build_spec = cros_build_spec
421 self._firmware_rw_build_spec = firmware_rw_build_spec
Dan Shib49bb8b2016-03-01 15:29:27 -0800422 self._firmware_ro_build_spec = firmware_ro_build_spec
Dan Shia25e0d42015-07-23 15:00:04 -0700423 self._test_source = test_source
Dan Shi29a16992015-09-22 11:29:58 -0700424 self._job_retry = job_retry
Dan Shi2121a332016-02-25 14:22:22 -0800425 self._hour = hour
426 self._day = day
427 self._os_type = os_type
428 self._launch_control_branches = (
429 [b.strip() for b in launch_control_branches.split(',')]
430 if launch_control_branches else [])
431 self._launch_control_targets = (
432 [t.strip() for t in launch_control_targets.split(',')]
433 if launch_control_targets else [])
Dan Shia25e0d42015-07-23 15:00:04 -0700434
Dan Shib49bb8b2016-03-01 15:29:27 -0800435 if ((self._firmware_rw_build_spec or self._firmware_ro_build_spec or
436 cros_build_spec) and
437 not self.test_source in [Builds.FIRMWARE_RW, Builds.FIRMWARE_RO,
438 Builds.CROS]):
Dan Shia25e0d42015-07-23 15:00:04 -0700439 raise MalformedConfigEntry(
440 'You must specify the build for test source. It can only '
Dan Shib49bb8b2016-03-01 15:29:27 -0800441 'be `firmware_rw`, `firmware_ro` or `cros`.')
Dan Shia25e0d42015-07-23 15:00:04 -0700442 if self._firmware_rw_build_spec and cros_build_spec:
443 raise MalformedConfigEntry(
444 'You cannot specify both firmware_rw_build_spec and '
445 'cros_build_spec. firmware_rw_build_spec is used to specify'
446 ' a firmware build when the suite requires firmware to be '
Dan Shi59562a82016-01-06 16:05:31 -0800447 'updated in the dut, its value can only be `firmware` or '
448 '`cros`. cros_build_spec is used to specify a ChromeOS '
449 'build when build_specs is set to firmware.')
Dan Shia25e0d42015-07-23 15:00:04 -0700450 if (self._firmware_rw_build_spec and
Dan Shi59562a82016-01-06 16:05:31 -0800451 self._firmware_rw_build_spec not in ['firmware', 'cros']):
Dan Shia25e0d42015-07-23 15:00:04 -0700452 raise MalformedConfigEntry(
Dan Shi59562a82016-01-06 16:05:31 -0800453 'firmware_rw_build_spec can only be empty, firmware or '
454 'cros. It does not support other build type yet.')
Chris Masone96f16632012-04-04 18:36:03 -0700455
456 self._bare_branches = []
Dan Shiaceb91d2013-02-20 12:41:28 -0800457 self._version_equal_constraint = False
Dan Shibde10772015-08-18 10:15:58 -0700458 self._version_gte_constraint = False
459 self._version_lte_constraint = False
Chris Masone67f06d62012-04-12 15:16:56 -0700460 if not branch_specs:
461 # Any milestone is OK.
462 self._numeric_constraint = version.LooseVersion('0')
463 else:
464 self._numeric_constraint = None
465 for spec in branch_specs:
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700466 if 'tot' in spec.lower():
467 tot_str = spec[spec.index('tot'):]
468 spec = spec.replace(
469 tot_str, TotMilestoneManager().ConvertTotSpec(
Dan Shibde10772015-08-18 10:15:58 -0700470 tot_str))
Chris Masone67f06d62012-04-12 15:16:56 -0700471 if spec.startswith('>='):
472 self._numeric_constraint = version.LooseVersion(
Dan Shibde10772015-08-18 10:15:58 -0700473 spec.lstrip('>=R'))
474 self._version_gte_constraint = True
475 elif spec.startswith('<='):
476 self._numeric_constraint = version.LooseVersion(
477 spec.lstrip('<=R'))
478 self._version_lte_constraint = True
Dan Shiaceb91d2013-02-20 12:41:28 -0800479 elif spec.startswith('=='):
480 self._version_equal_constraint = True
481 self._numeric_constraint = version.LooseVersion(
Dan Shibde10772015-08-18 10:15:58 -0700482 spec.lstrip('==R'))
Chris Masone67f06d62012-04-12 15:16:56 -0700483 else:
484 self._bare_branches.append(spec)
Alex Millerf2b57442013-09-07 18:40:02 -0700485
Aviv Keshet52c7a212015-12-07 15:27:22 -0800486 # Since we expect __hash__() and other comparator methods to be used
Chris Masonefad911a2012-03-29 12:30:26 -0700487 # frequently by set operations, and they use str() a lot, pre-compute
488 # the string representation of this object.
Aviv Keshet8ce3c982013-01-24 09:23:13 -0800489 if num is None:
490 numStr = '[Default num]'
491 else:
492 numStr = '%d' % num
Alex Millerf2b57442013-09-07 18:40:02 -0700493
494 if boards is None:
495 self._boards = set()
496 boardsStr = '[All boards]'
497 else:
498 self._boards = set([x.strip() for x in boards.split(',')])
499 boardsStr = boards
500
Dan Shi481b8b62016-03-08 13:03:08 -0800501 time_str = ''
502 if self._hour:
503 time_str = ' Run at %d:00.' % self._hour
504 elif self._day:
505 time_str = ' Run on %s.' % _WEEKDAYS[self._day]
Dan Shi2121a332016-02-25 14:22:22 -0800506 if os_type == OS_TYPE_CROS:
507 self._str = ('%s: %s on %s with pool %s, boards [%s], file_bugs = '
Dan Shi481b8b62016-03-08 13:03:08 -0800508 '%s across %s machines.%s' %
Dan Shi2121a332016-02-25 14:22:22 -0800509 (self.__class__.__name__, suite, branch_specs, pool,
Dan Shi481b8b62016-03-08 13:03:08 -0800510 boardsStr, self._file_bugs, numStr, time_str))
Dan Shi2121a332016-02-25 14:22:22 -0800511 else:
512 self._str = ('%s: %s on branches %s and targets %s with pool %s, '
Dan Shi481b8b62016-03-08 13:03:08 -0800513 'boards [%s], file_bugs = %s across %s machines.%s' %
Dan Shi2121a332016-02-25 14:22:22 -0800514 (self.__class__.__name__, suite,
515 launch_control_branches, launch_control_targets,
Dan Shi481b8b62016-03-08 13:03:08 -0800516 pool, boardsStr, self._file_bugs, numStr, time_str))
517
Chris Masone96f16632012-04-04 18:36:03 -0700518
519
520 def _FitsSpec(self, branch):
521 """Checks if a branch is deemed OK by this instance's branch specs.
522
523 When called on a branch name, will return whether that branch
Dan Shiaceb91d2013-02-20 12:41:28 -0800524 'fits' the specifications stored in self._bare_branches,
Dan Shibde10772015-08-18 10:15:58 -0700525 self._numeric_constraint, self._version_equal_constraint,
526 self._version_gte_constraint and self._version_lte_constraint.
Chris Masone96f16632012-04-04 18:36:03 -0700527
528 @param branch: the branch to check.
529 @return True if b 'fits' with stored specs, False otherwise.
530 """
Chris Masone657e1552012-05-30 17:06:20 -0700531 if branch in BARE_BRANCHES:
532 return branch in self._bare_branches
Dan Shiaceb91d2013-02-20 12:41:28 -0800533 if self._numeric_constraint:
534 if self._version_equal_constraint:
535 return version.LooseVersion(branch) == self._numeric_constraint
Dan Shibde10772015-08-18 10:15:58 -0700536 elif self._version_gte_constraint:
537 return version.LooseVersion(branch) >= self._numeric_constraint
538 elif self._version_lte_constraint:
539 return version.LooseVersion(branch) <= self._numeric_constraint
Dan Shiaceb91d2013-02-20 12:41:28 -0800540 else:
Dan Shibde10772015-08-18 10:15:58 -0700541 # Default to great or equal constraint.
Dan Shiaceb91d2013-02-20 12:41:28 -0800542 return version.LooseVersion(branch) >= self._numeric_constraint
543 else:
544 return False
Chris Masonefad911a2012-03-29 12:30:26 -0700545
546
547 @property
Alex Miller9979b5a2012-11-01 17:36:12 -0700548 def name(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800549 """Name of this task, e.g. 'NightlyPower'."""
Alex Miller9979b5a2012-11-01 17:36:12 -0700550 return self._name
551
552
553 @property
Chris Masonefad911a2012-03-29 12:30:26 -0700554 def suite(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800555 """Name of the suite to run, e.g. 'bvt'."""
Chris Masonefad911a2012-03-29 12:30:26 -0700556 return self._suite
557
558
559 @property
Chris Masone96f16632012-04-04 18:36:03 -0700560 def branch_specs(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800561 """a pre-vetted iterable of branch specifiers,
562 e.g. ['>=R18', 'factory']."""
Chris Masone96f16632012-04-04 18:36:03 -0700563 return self._branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700564
565
566 @property
Chris Masone3fba86f2012-04-03 10:06:56 -0700567 def pool(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800568 """The pool of machines to use for scheduling purposes."""
Chris Masone3fba86f2012-04-03 10:06:56 -0700569 return self._pool
Chris Masonefad911a2012-03-29 12:30:26 -0700570
571
Alex Miller9979b5a2012-11-01 17:36:12 -0700572 @property
573 def num(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800574 """The number of devices across which to shard the test suite.
575 Type: integer or None"""
Alex Miller9979b5a2012-11-01 17:36:12 -0700576 return self._num
577
578
Alex Millerf2b57442013-09-07 18:40:02 -0700579 @property
580 def boards(self):
581 """The boards on which to run this suite.
582 Type: Iterable of strings"""
583 return self._boards
584
585
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700586 @property
587 def priority(self):
588 """The priority of the suite"""
589 return self._priority
590
591
592 @property
593 def timeout(self):
594 """The maximum lifetime of the suite in hours."""
595 return self._timeout
596
597
Dan Shia25e0d42015-07-23 15:00:04 -0700598 @property
599 def cros_build_spec(self):
600 """The build spec of ChromeOS to test with a firmware build."""
601 return self._cros_build_spec
602
603
604 @property
605 def firmware_rw_build_spec(self):
Dan Shib49bb8b2016-03-01 15:29:27 -0800606 """The build spec of RW firmware to test with a ChromeOS build.
607
608 The value can be firmware or cros.
609 """
Dan Shia25e0d42015-07-23 15:00:04 -0700610 return self._firmware_rw_build_spec
611
612
613 @property
Dan Shib49bb8b2016-03-01 15:29:27 -0800614 def firmware_ro_build_spec(self):
615 """The build spec of RO firmware to test with a ChromeOS build.
616
617 The value can be stable, firmware or cros, where stable is the stable
618 firmware build retrieved from stable_version table.
619 """
620 return self._firmware_ro_build_spec
621
622
623 @property
Dan Shia25e0d42015-07-23 15:00:04 -0700624 def test_source(self):
Dan Shib49bb8b2016-03-01 15:29:27 -0800625 """Source of the test code, value can be `firmware_rw`, `firmware_ro` or
626 `cros`."""
Dan Shia25e0d42015-07-23 15:00:04 -0700627 return self._test_source
628
629
Dan Shi2121a332016-02-25 14:22:22 -0800630 @property
631 def hour(self):
632 """An integer specifying the hour that a nightly run should be triggered
633 """
634 return self._hour
635
636
637 @property
638 def day(self):
639 """An integer specifying the day of a week that a weekly run should be
640 triggered"""
641 return self._day
642
643
644 @property
645 def os_type(self):
646 """Type of OS, e.g., cros, brillo, android."""
647 return self._os_type
648
649
650 @property
651 def launch_control_branches(self):
652 """A list of Launch Control builds."""
653 return self._launch_control_branches
654
655
656 @property
657 def launch_control_targets(self):
658 """A list of Launch Control targets."""
659 return self._launch_control_targets
660
661
Chris Masonefad911a2012-03-29 12:30:26 -0700662 def __str__(self):
663 return self._str
664
665
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700666 def __repr__(self):
667 return self._str
668
669
Chris Masonefad911a2012-03-29 12:30:26 -0700670 def __lt__(self, other):
671 return str(self) < str(other)
672
673
674 def __le__(self, other):
675 return str(self) <= str(other)
676
677
678 def __eq__(self, other):
679 return str(self) == str(other)
680
681
682 def __ne__(self, other):
683 return str(self) != str(other)
684
685
686 def __gt__(self, other):
687 return str(self) > str(other)
688
689
690 def __ge__(self, other):
691 return str(self) >= str(other)
692
693
694 def __hash__(self):
695 """Allows instances to be correctly deduped when used in a set."""
696 return hash(str(self))
697
698
Dan Shia25e0d42015-07-23 15:00:04 -0700699 def _GetCrOSBuild(self, mv, board):
700 """Get the ChromeOS build name to test with firmware build.
701
702 The ChromeOS build to be used is determined by `self.cros_build_spec`.
703 Its value can be:
704 tot: use the latest ToT build.
705 tot-x: use the latest build in x milestone before ToT.
706 Rxx: use the latest build on xx milestone.
707
708 @param board: the board against which to run self._suite.
709 @param mv: an instance of manifest_versions.ManifestVersions.
710
711 @return: The ChromeOS build name to test with firmware build.
712
713 """
714 if not self.cros_build_spec:
715 return None
716 if self.cros_build_spec.startswith('tot'):
717 milestone = TotMilestoneManager().ConvertTotSpec(
718 self.cros_build_spec)[1:]
719 elif self.cros_build_spec.startswith('R'):
720 milestone = self.cros_build_spec[1:]
721 milestone, latest_manifest = mv.GetLatestManifest(
722 board, 'release', milestone=milestone)
723 latest_build = base_event.BuildName(board, 'release', milestone,
724 latest_manifest)
725 logging.debug('Found latest build of %s for spec %s: %s',
726 board, self.cros_build_spec, latest_build)
727 return latest_build
728
729
Dan Shib49bb8b2016-03-01 15:29:27 -0800730 def _GetFirmwareBuild(self, spec, mv, board):
731 """Get the firmware build name to test with ChromeOS build.
Dan Shia25e0d42015-07-23 15:00:04 -0700732
Dan Shib49bb8b2016-03-01 15:29:27 -0800733 @param spec: build spec for RO or RW firmware, e.g., firmware, cros
Dan Shia25e0d42015-07-23 15:00:04 -0700734 @param mv: an instance of manifest_versions.ManifestVersions.
735 @param board: the board against which to run self._suite.
Dan Shia25e0d42015-07-23 15:00:04 -0700736
Dan Shib49bb8b2016-03-01 15:29:27 -0800737 @return: The firmware build name to test with ChromeOS build.
Dan Shia25e0d42015-07-23 15:00:04 -0700738 """
Dan Shib49bb8b2016-03-01 15:29:27 -0800739 if spec == 'stable':
740 # TODO(crbug.com/577316): Query stable RO firmware.
741 raise NotImplementedError('`stable` RO firmware build is not '
742 'supported yet.')
743 if not spec:
Dan Shia25e0d42015-07-23 15:00:04 -0700744 return None
Dan Shib49bb8b2016-03-01 15:29:27 -0800745 # build_type is the build type of the firmware build, e.g., factory,
746 # firmware or release. If spec is set to cros, build type should be
747 # mapped to release.
748 build_type = 'release' if spec == 'cros' else spec
Dan Shia25e0d42015-07-23 15:00:04 -0700749 latest_milestone, latest_manifest = mv.GetLatestManifest(
750 board, build_type)
751 latest_build = base_event.BuildName(board, build_type, latest_milestone,
752 latest_manifest)
753 logging.debug('Found latest firmware build of %s for spec %s: %s',
Dan Shib49bb8b2016-03-01 15:29:27 -0800754 board, spec, latest_build)
Dan Shia25e0d42015-07-23 15:00:04 -0700755 return latest_build
756
757
Alex Miller7ce1b362012-07-10 09:24:10 -0700758 def AvailableHosts(self, scheduler, board):
759 """Query what hosts are able to run a test on a board and pool
760 combination.
Alex Miller511a9e32012-07-03 09:16:47 -0700761
762 @param scheduler: an instance of DedupingScheduler, as defined in
763 deduping_scheduler.py
764 @param board: the board against which one wants to run the test.
Alex Miller7ce1b362012-07-10 09:24:10 -0700765 @return The list of hosts meeting the board and pool requirements,
766 or None if no hosts were found."""
Alex Millerf2b57442013-09-07 18:40:02 -0700767 if self._boards and board not in self._boards:
768 return []
769
Alex Miller511a9e32012-07-03 09:16:47 -0700770 labels = [Labels.BOARD_PREFIX + board]
771 if self._pool:
Chris Masonecd214e02012-07-10 16:22:10 -0700772 labels.append(Labels.POOL_PREFIX + self._pool)
Alex Miller511a9e32012-07-03 09:16:47 -0700773
Prashanth B6de2bde2014-03-25 18:45:02 -0700774 return scheduler.CheckHostsExist(multiple_labels=labels)
Alex Miller511a9e32012-07-03 09:16:47 -0700775
776
Alex Millerd621cf22012-07-11 13:57:10 -0700777 def ShouldHaveAvailableHosts(self):
778 """As a sanity check, return true if we know for certain that
779 we should be able to schedule this test. If we claim this test
780 should be able to run, and it ends up not being scheduled, then
781 a warning will be reported.
782
783 @return True if this test should be able to run, False otherwise.
784 """
785 return self._pool == 'bvt'
786
787
Dan Shi2121a332016-02-25 14:22:22 -0800788 def _ScheduleSuite(self, scheduler, cros_build, firmware_rw_build,
Dan Shib49bb8b2016-03-01 15:29:27 -0800789 firmware_ro_build, test_source_build,
790 launch_control_build, board, force, run_prod_code=False):
Dan Shi2121a332016-02-25 14:22:22 -0800791 """Try to schedule a suite with given build and board information.
792
793 @param scheduler: an instance of DedupingScheduler, as defined in
794 deduping_scheduler.py
795 @oaran build: Build to run suite for, e.g., 'daisy-release/R18-1655.0.0'
796 and 'git_mnc_release/shamu-eng/123'.
797 @param firmware_rw_build: Firmware RW build to run test with.
Dan Shib49bb8b2016-03-01 15:29:27 -0800798 @param firmware_ro_build: Firmware RO build to run test with.
Dan Shi2121a332016-02-25 14:22:22 -0800799 @param test_source_build: Test source build, used for server-side
800 packaging.
801 @param launch_control_build: Name of a Launch Control build, e.g.,
802 'git_mnc_release/shamu-eng/123'
803 @param board: the board against which to run self._suite.
804 @param force: Always schedule the suite.
805 @param run_prod_code: If True, the suite will run the test code that
806 lives in prod aka the test code currently on the
807 lab servers. If False, the control files and test
808 code for this suite run will be retrieved from the
809 build artifacts. Default is False.
810 """
811 test_source_build_msg = (
812 ' Test source build is %s.' % test_source_build
Dan Shi8446fac2016-03-02 22:07:39 -0800813 if test_source_build else '')
Dan Shi2121a332016-02-25 14:22:22 -0800814 firmware_rw_build_msg = (
815 ' Firmware RW build is %s.' % firmware_rw_build
Dan Shi8446fac2016-03-02 22:07:39 -0800816 if firmware_rw_build else '')
Dan Shib49bb8b2016-03-01 15:29:27 -0800817 firmware_ro_build_msg = (
818 ' Firmware RO build is %s.' % firmware_ro_build
Dan Shi8446fac2016-03-02 22:07:39 -0800819 if firmware_ro_build else '')
Dan Shi2121a332016-02-25 14:22:22 -0800820 build_string = cros_build or launch_control_build
Dan Shib49bb8b2016-03-01 15:29:27 -0800821 logging.debug('Schedule %s for build %s.%s%s%s',
Dan Shi2121a332016-02-25 14:22:22 -0800822 self._suite, build_string, test_source_build_msg,
Dan Shib49bb8b2016-03-01 15:29:27 -0800823 firmware_rw_build_msg, firmware_ro_build_msg)
Dan Shi2121a332016-02-25 14:22:22 -0800824
825 if not scheduler.ScheduleSuite(
826 self._suite, board, cros_build, self._pool, self._num,
827 self._priority, self._timeout, force,
828 file_bugs=self._file_bugs,
829 firmware_rw_build=firmware_rw_build,
Dan Shib49bb8b2016-03-01 15:29:27 -0800830 firmware_ro_build=firmware_ro_build,
Dan Shi2121a332016-02-25 14:22:22 -0800831 test_source_build=test_source_build,
832 job_retry=self._job_retry,
833 launch_control_build=launch_control_build,
834 run_prod_code=run_prod_code):
835 logging.info('Skipping scheduling %s on %s for %s',
836 self._suite, build_string, board)
837
838
839 def _Run_CrOS_Builds(self, scheduler, branch_builds, board, force=False,
840 mv=None):
841 """Run this task for CrOS builds. Returns False if it should be
842 destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -0700843
Chris Masone013859b2012-04-01 13:45:26 -0700844 Execute this task. Attempt to schedule the associated suite.
845 Return True if this task should be kept around, False if it
846 should be destroyed. This allows for one-shot Tasks.
Chris Masonefad911a2012-03-29 12:30:26 -0700847
848 @param scheduler: an instance of DedupingScheduler, as defined in
849 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -0700850 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -0700851 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -0700852 {'R18': ['x86-alex-release/R18-1655.0.0'],
853 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -0700854 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -0700855 @param force: Always schedule the suite.
Dan Shia25e0d42015-07-23 15:00:04 -0700856 @param mv: an instance of manifest_versions.ManifestVersions.
857
Chris Masone013859b2012-04-01 13:45:26 -0700858 @return True if the task should be kept, False if not
Dan Shia25e0d42015-07-23 15:00:04 -0700859
Chris Masonefad911a2012-03-29 12:30:26 -0700860 """
Chris Masonea3a38172012-05-14 15:19:56 -0700861 logging.info('Running %s on %s', self._name, board)
Dan Shia25e0d42015-07-23 15:00:04 -0700862 is_firmware_build = 'firmware' in self.branch_specs
Dan Shib49bb8b2016-03-01 15:29:27 -0800863
864 # firmware_xx_build is only needed if firmware_xx_build_spec is given.
Dan Shia25e0d42015-07-23 15:00:04 -0700865 firmware_rw_build = None
Dan Shib49bb8b2016-03-01 15:29:27 -0800866 firmware_ro_build = None
Dan Shia25e0d42015-07-23 15:00:04 -0700867 try:
868 if is_firmware_build:
869 # When build specified in branch_specs is a firmware build,
870 # we need a ChromeOS build to test with the firmware build.
871 cros_build = self._GetCrOSBuild(mv, board)
Dan Shib49bb8b2016-03-01 15:29:27 -0800872 elif self.firmware_rw_build_spec or self.firmware_ro_build_spec:
873 # When firmware_xx_build_spec is specified, the test involves
874 # updating the RW firmware by firmware build specified in
875 # firmware_xx_build_spec.
876 firmware_rw_build = self._GetFirmwareBuild(
877 self.firmware_rw_build_spec, mv, board)
878 firmware_ro_build = self._GetFirmwareBuild(
879 self.firmware_ro_build_spec, mv, board)
Dan Shia25e0d42015-07-23 15:00:04 -0700880 except manifest_versions.QueryException as e:
881 logging.error(e)
882 logging.error('Running %s on %s is failed. Failed to find build '
883 'required to run the suite.', self._name, board)
884 return False
885
Chris Masone96f16632012-04-04 18:36:03 -0700886 builds = []
Chris Masonefe5a5092012-04-11 18:29:07 -0700887 for branch, build in branch_builds.iteritems():
Chris Masonea3a38172012-05-14 15:19:56 -0700888 logging.info('Checking if %s fits spec %r',
889 branch, self.branch_specs)
Chris Masone96f16632012-04-04 18:36:03 -0700890 if self._FitsSpec(branch):
Dan Shibde10772015-08-18 10:15:58 -0700891 logging.debug('Build %s fits the spec.', build)
Chris Masone05b19442012-04-17 13:37:55 -0700892 builds.extend(build)
Chris Masone96f16632012-04-04 18:36:03 -0700893 for build in builds:
Chris Masone3fba86f2012-04-03 10:06:56 -0700894 try:
Dan Shia25e0d42015-07-23 15:00:04 -0700895 if is_firmware_build:
896 firmware_rw_build = build
897 else:
898 cros_build = build
899 if self.test_source == Builds.FIRMWARE_RW:
900 test_source_build = firmware_rw_build
901 elif self.test_source == Builds.CROS:
902 test_source_build = cros_build
903 else:
904 test_source_build = None
Dan Shi2121a332016-02-25 14:22:22 -0800905 self._ScheduleSuite(scheduler, cros_build, firmware_rw_build,
Dan Shib49bb8b2016-03-01 15:29:27 -0800906 firmware_ro_build, test_source_build,
907 None, board, force)
Chris Masone3fba86f2012-04-03 10:06:56 -0700908 except deduping_scheduler.DedupingSchedulerException as e:
909 logging.error(e)
Chris Masonefad911a2012-03-29 12:30:26 -0700910 return True
911
912
Dan Shi2121a332016-02-25 14:22:22 -0800913 def _Run_LaunchControl_Builds(self, scheduler, launch_control_builds, board,
914 force=False):
915 """Run this task. Returns False if it should be destroyed.
916
917 Execute this task. Attempt to schedule the associated suite.
918 Return True if this task should be kept around, False if it
919 should be destroyed. This allows for one-shot Tasks.
920
921 @param scheduler: an instance of DedupingScheduler, as defined in
922 deduping_scheduler.py
923 @param launch_control_builds: A list of Launch Control builds.
924 @param board: the board against which to run self._suite.
925 @param force: Always schedule the suite.
926
927 @return True if the task should be kept, False if not
928
929 """
930 logging.info('Running %s on %s', self._name, board)
931 for build in launch_control_builds:
932 try:
Dan Shi8446fac2016-03-02 22:07:39 -0800933 self._ScheduleSuite(scheduler, None, None, None,
934 test_source_build=build,
Dan Shi2121a332016-02-25 14:22:22 -0800935 launch_control_build=build, board=board,
936 force=force, run_prod_code=True)
937 except deduping_scheduler.DedupingSchedulerException as e:
938 logging.error(e)
939 return True
940
941
942 def Run(self, scheduler, branch_builds, board, force=False, mv=None,
943 launch_control_builds=None):
944 """Run this task. Returns False if it should be destroyed.
945
946 Execute this task. Attempt to schedule the associated suite.
947 Return True if this task should be kept around, False if it
948 should be destroyed. This allows for one-shot Tasks.
949
950 @param scheduler: an instance of DedupingScheduler, as defined in
951 deduping_scheduler.py
952 @param branch_builds: a dict mapping branch name to the build(s) to
953 install for that branch, e.g.
954 {'R18': ['x86-alex-release/R18-1655.0.0'],
955 'R19': ['x86-alex-release/R19-2077.0.0']}
956 @param board: the board against which to run self._suite.
957 @param force: Always schedule the suite.
958 @param mv: an instance of manifest_versions.ManifestVersions.
959 @param launch_control_builds: A list of Launch Control builds.
960
961 @return True if the task should be kept, False if not
962
963 """
964 if ((self._os_type == OS_TYPE_CROS and not branch_builds) or
965 (self._os_type != OS_TYPE_CROS and not launch_control_builds)):
966 logging.debug('No build to run, skip running %s on %s.', self._name,
967 board)
968 # Return True so the task will be kept, as the given build and board
969 # do not match.
970 return True
971
972 if self._os_type == OS_TYPE_CROS:
973 return self._Run_CrOS_Builds(
974 scheduler, branch_builds, board, force, mv)
975 else:
976 return self._Run_LaunchControl_Builds(
977 scheduler, launch_control_builds, board, force)
978
979
Chris Masone013859b2012-04-01 13:45:26 -0700980class OneShotTask(Task):
981 """A Task that can be run only once. Can schedule itself."""
Chris Masonefad911a2012-03-29 12:30:26 -0700982
983
Dan Shi2121a332016-02-25 14:22:22 -0800984 def Run(self, scheduler, branch_builds, board, force=False, mv=None,
985 launch_control_builds=None):
Chris Masone013859b2012-04-01 13:45:26 -0700986 """Run this task. Returns False, indicating it should be destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -0700987
Chris Masone013859b2012-04-01 13:45:26 -0700988 Run this task. Attempt to schedule the associated suite.
989 Return False, indicating to the caller that it should discard this task.
Chris Masonefad911a2012-03-29 12:30:26 -0700990
991 @param scheduler: an instance of DedupingScheduler, as defined in
992 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -0700993 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -0700994 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -0700995 {'R18': ['x86-alex-release/R18-1655.0.0'],
996 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -0700997 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -0700998 @param force: Always schedule the suite.
Dan Shia25e0d42015-07-23 15:00:04 -0700999 @param mv: an instance of manifest_versions.ManifestVersions.
Dan Shi2121a332016-02-25 14:22:22 -08001000 @param launch_control_builds: A list of Launch Control builds.
Dan Shia25e0d42015-07-23 15:00:04 -07001001
Chris Masonefad911a2012-03-29 12:30:26 -07001002 @return False
Dan Shia25e0d42015-07-23 15:00:04 -07001003
Chris Masonefad911a2012-03-29 12:30:26 -07001004 """
Dan Shia25e0d42015-07-23 15:00:04 -07001005 super(OneShotTask, self).Run(scheduler, branch_builds, board, force,
Dan Shi2121a332016-02-25 14:22:22 -08001006 mv, launch_control_builds)
Chris Masonefad911a2012-03-29 12:30:26 -07001007 return False