blob: c14c4007583542ac91e7e5af4bce2cad6a6f612e [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
Dan Shi648ce922016-03-26 00:22:47 -070019from autotest_lib.client.common_lib import global_config
Fang Dengf08814a2015-08-03 18:12:18 +000020from autotest_lib.server import utils as server_utils
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070021from autotest_lib.server.cros.dynamic_suite import constants
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070022
Dan Shi648ce922016-03-26 00:22:47 -070023
24CONFIG = global_config.global_config
25
Dan Shi2121a332016-02-25 14:22:22 -080026OS_TYPE_CROS = 'cros'
27OS_TYPE_BRILLO = 'brillo'
28OS_TYPE_ANDROID = 'android'
29OS_TYPES = {OS_TYPE_CROS, OS_TYPE_BRILLO, OS_TYPE_ANDROID}
30OS_TYPES_LAUNCH_CONTROL = {OS_TYPE_BRILLO, OS_TYPE_ANDROID}
Chris Masone96f16632012-04-04 18:36:03 -070031
Dan Shi481b8b62016-03-08 13:03:08 -080032_WEEKDAYS = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday',
33 'Sunday']
34
Chris Masone96f16632012-04-04 18:36:03 -070035class MalformedConfigEntry(Exception):
36 """Raised to indicate a failure to parse a Task out of a config."""
37 pass
Chris Masonefad911a2012-03-29 12:30:26 -070038
39
Alex Miller0d003572013-03-18 11:51:30 -070040BARE_BRANCHES = ['factory', 'firmware']
Chris Masone67f06d62012-04-12 15:16:56 -070041
42
43def PickBranchName(type, milestone):
Dan Shiaceb91d2013-02-20 12:41:28 -080044 """Pick branch name. If type is among BARE_BRANCHES, return type,
45 otherwise, return milestone.
46
47 @param type: type of the branch, e.g., 'release', 'factory', or 'firmware'
48 @param milestone: CrOS milestone number
49 """
Chris Masone67f06d62012-04-12 15:16:56 -070050 if type in BARE_BRANCHES:
51 return type
52 return milestone
53
54
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070055class TotMilestoneManager(object):
56 """A class capable of converting tot string to milestone numbers.
57
58 This class is used as a cache for the tot milestone, so we don't
59 repeatedly hit google storage for all O(100) tasks in suite
60 scheduler's ini file.
61 """
62
Fang Dengf08814a2015-08-03 18:12:18 +000063 __metaclass__ = server_utils.Singleton
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070064
Dan Shi23245142015-01-22 13:22:28 -080065 # True if suite_scheduler is running for sanity check. When it's set to
66 # True, the code won't make gsutil call to get the actual tot milestone to
67 # avoid dependency on the installation of gsutil to run sanity check.
68 is_sanity = False
69
70
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070071 @staticmethod
72 def _tot_milestone():
73 """Get the tot milestone, eg: R40
74
75 @returns: A string representing the Tot milestone as declared by
76 the LATEST_BUILD_URL, or an empty string if LATEST_BUILD_URL
77 doesn't exist.
78 """
Dan Shi23245142015-01-22 13:22:28 -080079 if TotMilestoneManager.is_sanity:
80 logging.info('suite_scheduler is running for sanity purpose, no '
81 'need to get the actual tot milestone string.')
82 return 'R40'
83
Prashanth Balasubramanian1b859622014-10-28 16:02:15 -070084 cmd = ['gsutil', 'cat', constants.LATEST_BUILD_URL]
85 proc = subprocess.Popen(
86 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
87 stdout, stderr = proc.communicate()
88 if proc.poll():
89 logging.warning('Failed to get latest build: %s', stderr)
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070090 return ''
Prashanth Balasubramanian1b859622014-10-28 16:02:15 -070091 return stdout.split('-')[0]
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070092
93
94 def refresh(self):
95 """Refresh the tot milestone string managed by this class."""
96 self.tot = self._tot_milestone()
97
98
99 def __init__(self):
100 """Initialize a TotMilestoneManager."""
101 self.refresh()
102
103
104 def ConvertTotSpec(self, tot_spec):
105 """Converts a tot spec to the appropriate milestone.
106
107 Assume tot is R40:
108 tot -> R40
109 tot-1 -> R39
110 tot-2 -> R38
111 tot-(any other numbers) -> R40
112
113 With the last option one assumes that a malformed configuration that has
114 'tot' in it, wants at least tot.
115
116 @param tot_spec: A string representing the tot spec.
117 @raises MalformedConfigEntry: If the tot_spec doesn't match the
118 expected format.
119 """
120 tot_spec = tot_spec.lower()
121 match = re.match('(tot)[-]?(1$|2$)?', tot_spec)
122 if not match:
123 raise MalformedConfigEntry(
124 "%s isn't a valid branch spec." % tot_spec)
125 tot_mstone = self.tot
126 num_back = match.groups()[1]
127 if num_back:
128 tot_mstone_num = tot_mstone.lstrip('R')
129 tot_mstone = tot_mstone.replace(
130 tot_mstone_num, str(int(tot_mstone_num)-int(num_back)))
131 return tot_mstone
132
133
Chris Masone013859b2012-04-01 13:45:26 -0700134class Task(object):
Chris Masonefad911a2012-03-29 12:30:26 -0700135 """Represents an entry from the scheduler config. Can schedule itself.
136
137 Each entry from the scheduler config file maps one-to-one to a
Chris Masone013859b2012-04-01 13:45:26 -0700138 Task. Each instance has enough info to schedule itself
Chris Masonefad911a2012-03-29 12:30:26 -0700139 on-demand with the AFE.
140
Aviv Keshet52c7a212015-12-07 15:27:22 -0800141 This class also overrides __hash__() and all comparator methods to enable
Chris Masonefad911a2012-03-29 12:30:26 -0700142 correct use in dicts, sets, etc.
143 """
144
Chris Masone96f16632012-04-04 18:36:03 -0700145
146 @staticmethod
147 def CreateFromConfigSection(config, section):
148 """Create a Task from a section of a config file.
149
150 The section to parse should look like this:
151 [TaskName]
152 suite: suite_to_run # Required
153 run_on: event_on which to run # Required
Dan Shi9f256d92016-01-22 00:09:25 -0800154 hour: integer of the hour to run, only applies to nightly. # Optional
Dan Shiaceb91d2013-02-20 12:41:28 -0800155 branch_specs: factory,firmware,>=R12 or ==R12 # Optional
Chris Masone96f16632012-04-04 18:36:03 -0700156 pool: pool_of_devices # Optional
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700157 num: sharding_factor # int, Optional
Alex Millerf2b57442013-09-07 18:40:02 -0700158 boards: board1, board2 # comma seperated string, Optional
Dan Shi2121a332016-02-25 14:22:22 -0800159 # Settings for Launch Control builds only:
160 os_type: brillo # Type of OS, e.g., cros, brillo, android. Default is
161 cros. Required for android/brillo builds.
162 branches: git_mnc_release # comma separated string of Launch Control
163 branches. Required and only applicable for android/brillo
164 builds.
165 targets: dragonboard-eng # comma separated string of build targets.
166 Required and only applicable for android/brillo builds.
Chris Masone96f16632012-04-04 18:36:03 -0700167
Chris Masone67f06d62012-04-12 15:16:56 -0700168 By default, Tasks run on all release branches, not factory or firmware.
169
Chris Masone96f16632012-04-04 18:36:03 -0700170 @param config: a ForgivingConfigParser.
171 @param section: the section to parse into a Task.
172 @return keyword, Task object pair. One or both will be None on error.
173 @raise MalformedConfigEntry if there's a problem parsing |section|.
174 """
Alex Miller06695022012-07-18 09:31:36 -0700175 if not config.has_section(section):
176 raise MalformedConfigEntry('unknown section %s' % section)
177
Alex Millerf2b57442013-09-07 18:40:02 -0700178 allowed = set(['suite', 'run_on', 'branch_specs', 'pool', 'num',
Dan Shia25e0d42015-07-23 15:00:04 -0700179 'boards', 'file_bugs', 'cros_build_spec',
Dan Shib49bb8b2016-03-01 15:29:27 -0800180 'firmware_rw_build_spec', 'firmware_ro_build_spec',
181 'test_source', 'job_retry', 'hour', 'day', 'branches',
182 'targets', 'os_type'])
Alex Millerbb535e22012-07-11 20:11:33 -0700183 # The parameter of union() is the keys under the section in the config
184 # The union merges this with the allowed set, so if any optional keys
185 # are omitted, then they're filled in. If any extra keys are present,
186 # then they will expand unioned set, causing it to fail the following
187 # comparison against the allowed set.
188 section_headers = allowed.union(dict(config.items(section)).keys())
189 if allowed != section_headers:
190 raise MalformedConfigEntry('unknown entries: %s' %
191 ", ".join(map(str, section_headers.difference(allowed))))
192
Chris Masone96f16632012-04-04 18:36:03 -0700193 keyword = config.getstring(section, 'run_on')
Dan Shi9f256d92016-01-22 00:09:25 -0800194 hour = config.getstring(section, 'hour')
Chris Masone96f16632012-04-04 18:36:03 -0700195 suite = config.getstring(section, 'suite')
Dan Shi2121a332016-02-25 14:22:22 -0800196 branch_specs = config.getstring(section, 'branch_specs')
Chris Masone96f16632012-04-04 18:36:03 -0700197 pool = config.getstring(section, 'pool')
Alex Millerf2b57442013-09-07 18:40:02 -0700198 boards = config.getstring(section, 'boards')
Prashanth B6de2bde2014-03-25 18:45:02 -0700199 file_bugs = config.getboolean(section, 'file_bugs')
Dan Shia25e0d42015-07-23 15:00:04 -0700200 cros_build_spec = config.getstring(section, 'cros_build_spec')
201 firmware_rw_build_spec = config.getstring(
202 section, 'firmware_rw_build_spec')
Dan Shib49bb8b2016-03-01 15:29:27 -0800203 firmware_ro_build_spec = config.getstring(
204 section, 'firmware_ro_build_spec')
Dan Shia25e0d42015-07-23 15:00:04 -0700205 test_source = config.getstring(section, 'test_source')
Dan Shi29a16992015-09-22 11:29:58 -0700206 job_retry = config.getboolean(section, 'job_retry')
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700207 for klass in driver.Driver.EVENT_CLASSES:
208 if klass.KEYWORD == keyword:
209 priority = klass.PRIORITY
210 timeout = klass.TIMEOUT
211 break
212 else:
213 priority = None
214 timeout = None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700215 try:
216 num = config.getint(section, 'num')
217 except ValueError as e:
Dan Shi9f256d92016-01-22 00:09:25 -0800218 raise MalformedConfigEntry("Ill-specified 'num': %r" % e)
Chris Masone96f16632012-04-04 18:36:03 -0700219 if not keyword:
220 raise MalformedConfigEntry('No event to |run_on|.')
221 if not suite:
222 raise MalformedConfigEntry('No |suite|')
Dan Shi9f256d92016-01-22 00:09:25 -0800223 try:
224 hour = config.getint(section, 'hour')
225 except ValueError as e:
226 raise MalformedConfigEntry("Ill-specified 'hour': %r" % e)
227 if hour is not None and (hour < 0 or hour > 23):
228 raise MalformedConfigEntry(
229 '`hour` must be an integer between 0 and 23.')
230 if hour is not None and keyword != 'nightly':
231 raise MalformedConfigEntry(
232 '`hour` is the trigger time that can only apply to nightly '
233 'event.')
Dan Shice1f20a2016-01-25 17:27:40 -0800234
235 try:
236 day = config.getint(section, 'day')
237 except ValueError as e:
238 raise MalformedConfigEntry("Ill-specified 'day': %r" % e)
239 if day is not None and (day < 0 or day > 6):
240 raise MalformedConfigEntry(
241 '`day` must be an integer between 0 and 6, where 0 is for '
242 'Monday and 6 is for Sunday.')
243 if day is not None and keyword != 'weekly':
244 raise MalformedConfigEntry(
245 '`day` is the trigger of the day of a week, that can only '
246 'apply to weekly events.')
247
Chris Masone96f16632012-04-04 18:36:03 -0700248 specs = []
Dan Shi2121a332016-02-25 14:22:22 -0800249 if branch_specs:
250 specs = re.split('\s*,\s*', branch_specs)
Chris Masone96f16632012-04-04 18:36:03 -0700251 Task.CheckBranchSpecs(specs)
Dan Shi2121a332016-02-25 14:22:22 -0800252
253 os_type = config.getstring(section, 'os_type') or OS_TYPE_CROS
254 if os_type not in OS_TYPES:
255 raise MalformedConfigEntry('`os_type` must be one of %s' % OS_TYPES)
256
257 lc_branches = config.getstring(section, 'branches')
258 lc_targets = config.getstring(section, 'targets')
259 if os_type == OS_TYPE_CROS and (lc_branches or lc_targets):
260 raise MalformedConfigEntry(
261 '`branches` and `targets` are only supported for Launch '
262 'Control builds, not ChromeOS builds.')
263 if (os_type in OS_TYPES_LAUNCH_CONTROL and
264 (not lc_branches or not lc_targets)):
265 raise MalformedConfigEntry(
266 '`branches` and `targets` must be specified for Launch '
267 'Control builds.')
268 if os_type in OS_TYPES_LAUNCH_CONTROL and boards:
269 raise MalformedConfigEntry(
270 '`boards` for Launch Control builds are retrieved from '
271 '`targets` setting, it should not be set for Launch '
272 'Control builds.')
273
274 # Extract boards from targets list.
275 if os_type in OS_TYPES_LAUNCH_CONTROL:
276 boards = ''
277 for target in lc_targets.split(','):
278 board_name, _ = server_utils.parse_launch_control_target(
279 target.strip())
Dan Shi8db7e612016-07-21 12:55:16 -0700280 boards += '%s,' % board_name
Dan Shi2121a332016-02-25 14:22:22 -0800281 boards = boards.strip(',')
282
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700283 return keyword, Task(section, suite, specs, pool, num, boards,
Prashanth B6de2bde2014-03-25 18:45:02 -0700284 priority, timeout,
Dan Shia25e0d42015-07-23 15:00:04 -0700285 file_bugs=file_bugs if file_bugs else False,
286 cros_build_spec=cros_build_spec,
287 firmware_rw_build_spec=firmware_rw_build_spec,
Dan Shib49bb8b2016-03-01 15:29:27 -0800288 firmware_ro_build_spec=firmware_ro_build_spec,
Dan Shi9f256d92016-01-22 00:09:25 -0800289 test_source=test_source, job_retry=job_retry,
Dan Shi2121a332016-02-25 14:22:22 -0800290 hour=hour, day=day, os_type=os_type,
291 launch_control_branches=lc_branches,
292 launch_control_targets=lc_targets)
Chris Masone96f16632012-04-04 18:36:03 -0700293
294
295 @staticmethod
296 def CheckBranchSpecs(branch_specs):
297 """Make sure entries in the list branch_specs are correctly formed.
298
Chris Masone67f06d62012-04-12 15:16:56 -0700299 We accept any of BARE_BRANCHES in |branch_specs|, as
Dan Shiaceb91d2013-02-20 12:41:28 -0800300 well as _one_ string of the form '>=RXX' or '==RXX', where 'RXX' is a
Chris Masone96f16632012-04-04 18:36:03 -0700301 CrOS milestone number.
302
303 @param branch_specs: an iterable of branch specifiers.
304 @raise MalformedConfigEntry if there's a problem parsing |branch_specs|.
305 """
306 have_seen_numeric_constraint = False
307 for branch in branch_specs:
Chris Masone67f06d62012-04-12 15:16:56 -0700308 if branch in BARE_BRANCHES:
Chris Masone96f16632012-04-04 18:36:03 -0700309 continue
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700310 if not have_seen_numeric_constraint:
311 #TODO(beeps): Why was <= dropped on the floor?
312 if branch.startswith('>=R') or branch.startswith('==R'):
313 have_seen_numeric_constraint = True
314 elif 'tot' in branch:
315 TotMilestoneManager().ConvertTotSpec(
316 branch[branch.index('tot'):])
317 have_seen_numeric_constraint = True
Chris Masone96f16632012-04-04 18:36:03 -0700318 continue
Chris Masone97cf0a72012-05-16 09:55:52 -0700319 raise MalformedConfigEntry("%s isn't a valid branch spec." % branch)
Chris Masone96f16632012-04-04 18:36:03 -0700320
321
Alex Millerf2b57442013-09-07 18:40:02 -0700322 def __init__(self, name, suite, branch_specs, pool=None, num=None,
Dan Shia25e0d42015-07-23 15:00:04 -0700323 boards=None, priority=None, timeout=None, file_bugs=False,
324 cros_build_spec=None, firmware_rw_build_spec=None,
Dan Shib49bb8b2016-03-01 15:29:27 -0800325 firmware_ro_build_spec=None, test_source=None, job_retry=False,
326 hour=None, day=None, os_type=OS_TYPE_CROS,
327 launch_control_branches=None, launch_control_targets=None):
Chris Masonefad911a2012-03-29 12:30:26 -0700328 """Constructor
329
Chris Masone96f16632012-04-04 18:36:03 -0700330 Given an iterable in |branch_specs|, pre-vetted using CheckBranchSpecs,
331 we'll store them such that _FitsSpec() can be used to check whether a
332 given branch 'fits' with the specifications passed in here.
333 For example, given branch_specs = ['factory', '>=R18'], we'd set things
334 up so that _FitsSpec() would return True for 'factory', or 'RXX'
Dan Shiaceb91d2013-02-20 12:41:28 -0800335 where XX is a number >= 18. Same check is done for branch_specs = [
336 'factory', '==R18'], which limit the test to only one specific branch.
Chris Masone96f16632012-04-04 18:36:03 -0700337
338 Given branch_specs = ['factory', 'firmware'], _FitsSpec()
339 would pass only those two specific strings.
340
341 Example usage:
Chris Masonecc4631d2012-04-20 12:06:39 -0700342 t = Task('Name', 'suite', ['factory', '>=R18'])
Chris Masone96f16632012-04-04 18:36:03 -0700343 t._FitsSpec('factory') # True
344 t._FitsSpec('R19') # True
345 t._FitsSpec('R17') # False
346 t._FitsSpec('firmware') # False
347 t._FitsSpec('goober') # False
348
Dan Shiaceb91d2013-02-20 12:41:28 -0800349 t = Task('Name', 'suite', ['factory', '==R18'])
350 t._FitsSpec('R19') # False, branch does not equal to 18
351 t._FitsSpec('R18') # True
352 t._FitsSpec('R17') # False
353
Dan Shia25e0d42015-07-23 15:00:04 -0700354 cros_build_spec and firmware_rw_build_spec are set for tests require
355 firmware update on the dut. Only one of them can be set.
356 For example:
357 branch_specs: ==tot
358 firmware_rw_build_spec: firmware
359 test_source: cros
360 This will run test using latest build on firmware branch, and the latest
361 ChromeOS build on ToT. The test source build is ChromeOS build.
362
363 branch_specs: firmware
364 cros_build_spec: ==tot-1
365 test_source: firmware_rw
366 This will run test using latest build on firmware branch, and the latest
367 ChromeOS build on dev channel (ToT-1). The test source build is the
368 firmware RW build.
369
Dan Shi59562a82016-01-06 16:05:31 -0800370 branch_specs: ==tot
371 firmware_rw_build_spec: cros
372 test_source: cros
373 This will run test using latest ChromeOS and firmware RW build on ToT.
374 ChromeOS build on ToT. The test source build is ChromeOS build.
375
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700376 @param name: name of this task, e.g. 'NightlyPower'
Chris Masonefad911a2012-03-29 12:30:26 -0700377 @param suite: the name of the suite to run, e.g. 'bvt'
Chris Masone96f16632012-04-04 18:36:03 -0700378 @param branch_specs: a pre-vetted iterable of branch specifiers,
379 e.g. ['>=R18', 'factory']
Chris Masonefad911a2012-03-29 12:30:26 -0700380 @param pool: the pool of machines to use for scheduling purposes.
381 Default: None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700382 @param num: the number of devices across which to shard the test suite.
Aviv Keshetd83ef442013-01-16 16:19:35 -0800383 Type: integer or None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700384 Default: None
Dan Shi9f256d92016-01-22 00:09:25 -0800385 @param boards: A comma separated list of boards to run this task on.
Alex Millerf2b57442013-09-07 18:40:02 -0700386 Default: Run on all boards.
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700387 @param priority: The string name of a priority from
388 client.common_lib.priorities.Priority.
389 @param timeout: The max lifetime of the suite in hours.
Prashanth B6de2bde2014-03-25 18:45:02 -0700390 @param file_bugs: True if bug filing is desired for the suite created
391 for this task.
Dan Shia25e0d42015-07-23 15:00:04 -0700392 @param cros_build_spec: Spec used to determine the ChromeOS build to
393 test with a firmware build, e.g., tot, R41 etc.
Dan Shib49bb8b2016-03-01 15:29:27 -0800394 @param firmware_rw_build_spec: Spec used to determine the firmware RW
395 build test with a ChromeOS build.
396 @param firmware_ro_build_spec: Spec used to determine the firmware RO
397 build test with a ChromeOS build.
Dan Shia25e0d42015-07-23 15:00:04 -0700398 @param test_source: The source of test code when firmware will be
Dan Shib49bb8b2016-03-01 15:29:27 -0800399 updated in the test. The value can be `firmware_rw`,
400 `firmware_ro` or `cros`.
Dan Shi29a16992015-09-22 11:29:58 -0700401 @param job_retry: Set to True to enable job-level retry. Default is
402 False.
Dan Shi9f256d92016-01-22 00:09:25 -0800403 @param hour: An integer specifying the hour that a nightly run should
404 be triggered, default is set to 21.
Dan Shice1f20a2016-01-25 17:27:40 -0800405 @param day: An integer specifying the day of a week that a weekly run
Dan Shi2121a332016-02-25 14:22:22 -0800406 should be triggered, default is set to 5, which is Saturday.
407 @param os_type: Type of OS, e.g., cros, brillo, android. Default is
408 cros. The argument is required for android/brillo builds.
409 @param launch_control_branches: Comma separated string of Launch Control
410 branches. The argument is required and only applicable for
411 android/brillo builds.
412 @param launch_control_targets: Comma separated string of build targets
413 for Launch Control builds. The argument is required and only
414 applicable for android/brillo builds.
Chris Masonefad911a2012-03-29 12:30:26 -0700415 """
Chris Masonecc4631d2012-04-20 12:06:39 -0700416 self._name = name
Chris Masonefad911a2012-03-29 12:30:26 -0700417 self._suite = suite
Chris Masone96f16632012-04-04 18:36:03 -0700418 self._branch_specs = branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700419 self._pool = pool
Aviv Keshetd83ef442013-01-16 16:19:35 -0800420 self._num = num
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700421 self._priority = priority
422 self._timeout = timeout
Prashanth B6de2bde2014-03-25 18:45:02 -0700423 self._file_bugs = file_bugs
Dan Shia25e0d42015-07-23 15:00:04 -0700424 self._cros_build_spec = cros_build_spec
425 self._firmware_rw_build_spec = firmware_rw_build_spec
Dan Shib49bb8b2016-03-01 15:29:27 -0800426 self._firmware_ro_build_spec = firmware_ro_build_spec
Dan Shia25e0d42015-07-23 15:00:04 -0700427 self._test_source = test_source
Dan Shi29a16992015-09-22 11:29:58 -0700428 self._job_retry = job_retry
Dan Shi2121a332016-02-25 14:22:22 -0800429 self._hour = hour
430 self._day = day
431 self._os_type = os_type
432 self._launch_control_branches = (
433 [b.strip() for b in launch_control_branches.split(',')]
434 if launch_control_branches else [])
435 self._launch_control_targets = (
436 [t.strip() for t in launch_control_targets.split(',')]
437 if launch_control_targets else [])
Dan Shia25e0d42015-07-23 15:00:04 -0700438
Dan Shib49bb8b2016-03-01 15:29:27 -0800439 if ((self._firmware_rw_build_spec or self._firmware_ro_build_spec or
440 cros_build_spec) and
441 not self.test_source in [Builds.FIRMWARE_RW, Builds.FIRMWARE_RO,
442 Builds.CROS]):
Dan Shia25e0d42015-07-23 15:00:04 -0700443 raise MalformedConfigEntry(
444 'You must specify the build for test source. It can only '
Dan Shib49bb8b2016-03-01 15:29:27 -0800445 'be `firmware_rw`, `firmware_ro` or `cros`.')
Dan Shia25e0d42015-07-23 15:00:04 -0700446 if self._firmware_rw_build_spec and cros_build_spec:
447 raise MalformedConfigEntry(
448 'You cannot specify both firmware_rw_build_spec and '
449 'cros_build_spec. firmware_rw_build_spec is used to specify'
450 ' a firmware build when the suite requires firmware to be '
Dan Shi59562a82016-01-06 16:05:31 -0800451 'updated in the dut, its value can only be `firmware` or '
452 '`cros`. cros_build_spec is used to specify a ChromeOS '
453 'build when build_specs is set to firmware.')
Dan Shia25e0d42015-07-23 15:00:04 -0700454 if (self._firmware_rw_build_spec and
Dan Shi59562a82016-01-06 16:05:31 -0800455 self._firmware_rw_build_spec not in ['firmware', 'cros']):
Dan Shia25e0d42015-07-23 15:00:04 -0700456 raise MalformedConfigEntry(
Dan Shi59562a82016-01-06 16:05:31 -0800457 'firmware_rw_build_spec can only be empty, firmware or '
458 'cros. It does not support other build type yet.')
Chris Masone96f16632012-04-04 18:36:03 -0700459
460 self._bare_branches = []
Dan Shiaceb91d2013-02-20 12:41:28 -0800461 self._version_equal_constraint = False
Dan Shibde10772015-08-18 10:15:58 -0700462 self._version_gte_constraint = False
463 self._version_lte_constraint = False
Chris Masone67f06d62012-04-12 15:16:56 -0700464 if not branch_specs:
465 # Any milestone is OK.
466 self._numeric_constraint = version.LooseVersion('0')
467 else:
468 self._numeric_constraint = None
469 for spec in branch_specs:
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700470 if 'tot' in spec.lower():
471 tot_str = spec[spec.index('tot'):]
472 spec = spec.replace(
473 tot_str, TotMilestoneManager().ConvertTotSpec(
Dan Shibde10772015-08-18 10:15:58 -0700474 tot_str))
Chris Masone67f06d62012-04-12 15:16:56 -0700475 if spec.startswith('>='):
476 self._numeric_constraint = version.LooseVersion(
Dan Shibde10772015-08-18 10:15:58 -0700477 spec.lstrip('>=R'))
478 self._version_gte_constraint = True
479 elif spec.startswith('<='):
480 self._numeric_constraint = version.LooseVersion(
481 spec.lstrip('<=R'))
482 self._version_lte_constraint = True
Dan Shiaceb91d2013-02-20 12:41:28 -0800483 elif spec.startswith('=='):
484 self._version_equal_constraint = True
485 self._numeric_constraint = version.LooseVersion(
Dan Shibde10772015-08-18 10:15:58 -0700486 spec.lstrip('==R'))
Chris Masone67f06d62012-04-12 15:16:56 -0700487 else:
488 self._bare_branches.append(spec)
Alex Millerf2b57442013-09-07 18:40:02 -0700489
Aviv Keshet52c7a212015-12-07 15:27:22 -0800490 # Since we expect __hash__() and other comparator methods to be used
Chris Masonefad911a2012-03-29 12:30:26 -0700491 # frequently by set operations, and they use str() a lot, pre-compute
492 # the string representation of this object.
Aviv Keshet8ce3c982013-01-24 09:23:13 -0800493 if num is None:
494 numStr = '[Default num]'
495 else:
496 numStr = '%d' % num
Alex Millerf2b57442013-09-07 18:40:02 -0700497
498 if boards is None:
499 self._boards = set()
500 boardsStr = '[All boards]'
501 else:
502 self._boards = set([x.strip() for x in boards.split(',')])
503 boardsStr = boards
504
Dan Shi481b8b62016-03-08 13:03:08 -0800505 time_str = ''
506 if self._hour:
507 time_str = ' Run at %d:00.' % self._hour
508 elif self._day:
509 time_str = ' Run on %s.' % _WEEKDAYS[self._day]
Dan Shi2121a332016-02-25 14:22:22 -0800510 if os_type == OS_TYPE_CROS:
511 self._str = ('%s: %s on %s with pool %s, boards [%s], file_bugs = '
Dan Shi481b8b62016-03-08 13:03:08 -0800512 '%s across %s machines.%s' %
Dan Shi2121a332016-02-25 14:22:22 -0800513 (self.__class__.__name__, suite, branch_specs, pool,
Dan Shi481b8b62016-03-08 13:03:08 -0800514 boardsStr, self._file_bugs, numStr, time_str))
Dan Shi2121a332016-02-25 14:22:22 -0800515 else:
516 self._str = ('%s: %s on branches %s and targets %s with pool %s, '
Dan Shi481b8b62016-03-08 13:03:08 -0800517 'boards [%s], file_bugs = %s across %s machines.%s' %
Dan Shi2121a332016-02-25 14:22:22 -0800518 (self.__class__.__name__, suite,
519 launch_control_branches, launch_control_targets,
Dan Shi481b8b62016-03-08 13:03:08 -0800520 pool, boardsStr, self._file_bugs, numStr, time_str))
521
Chris Masone96f16632012-04-04 18:36:03 -0700522
523
524 def _FitsSpec(self, branch):
525 """Checks if a branch is deemed OK by this instance's branch specs.
526
527 When called on a branch name, will return whether that branch
Dan Shiaceb91d2013-02-20 12:41:28 -0800528 'fits' the specifications stored in self._bare_branches,
Dan Shibde10772015-08-18 10:15:58 -0700529 self._numeric_constraint, self._version_equal_constraint,
530 self._version_gte_constraint and self._version_lte_constraint.
Chris Masone96f16632012-04-04 18:36:03 -0700531
532 @param branch: the branch to check.
533 @return True if b 'fits' with stored specs, False otherwise.
534 """
Chris Masone657e1552012-05-30 17:06:20 -0700535 if branch in BARE_BRANCHES:
536 return branch in self._bare_branches
Dan Shiaceb91d2013-02-20 12:41:28 -0800537 if self._numeric_constraint:
538 if self._version_equal_constraint:
539 return version.LooseVersion(branch) == self._numeric_constraint
Dan Shibde10772015-08-18 10:15:58 -0700540 elif self._version_gte_constraint:
541 return version.LooseVersion(branch) >= self._numeric_constraint
542 elif self._version_lte_constraint:
543 return version.LooseVersion(branch) <= self._numeric_constraint
Dan Shiaceb91d2013-02-20 12:41:28 -0800544 else:
Dan Shibde10772015-08-18 10:15:58 -0700545 # Default to great or equal constraint.
Dan Shiaceb91d2013-02-20 12:41:28 -0800546 return version.LooseVersion(branch) >= self._numeric_constraint
547 else:
548 return False
Chris Masonefad911a2012-03-29 12:30:26 -0700549
550
551 @property
Alex Miller9979b5a2012-11-01 17:36:12 -0700552 def name(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800553 """Name of this task, e.g. 'NightlyPower'."""
Alex Miller9979b5a2012-11-01 17:36:12 -0700554 return self._name
555
556
557 @property
Chris Masonefad911a2012-03-29 12:30:26 -0700558 def suite(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800559 """Name of the suite to run, e.g. 'bvt'."""
Chris Masonefad911a2012-03-29 12:30:26 -0700560 return self._suite
561
562
563 @property
Chris Masone96f16632012-04-04 18:36:03 -0700564 def branch_specs(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800565 """a pre-vetted iterable of branch specifiers,
566 e.g. ['>=R18', 'factory']."""
Chris Masone96f16632012-04-04 18:36:03 -0700567 return self._branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700568
569
570 @property
Chris Masone3fba86f2012-04-03 10:06:56 -0700571 def pool(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800572 """The pool of machines to use for scheduling purposes."""
Chris Masone3fba86f2012-04-03 10:06:56 -0700573 return self._pool
Chris Masonefad911a2012-03-29 12:30:26 -0700574
575
Alex Miller9979b5a2012-11-01 17:36:12 -0700576 @property
577 def num(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800578 """The number of devices across which to shard the test suite.
579 Type: integer or None"""
Alex Miller9979b5a2012-11-01 17:36:12 -0700580 return self._num
581
582
Alex Millerf2b57442013-09-07 18:40:02 -0700583 @property
584 def boards(self):
585 """The boards on which to run this suite.
586 Type: Iterable of strings"""
587 return self._boards
588
589
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700590 @property
591 def priority(self):
592 """The priority of the suite"""
593 return self._priority
594
595
596 @property
597 def timeout(self):
598 """The maximum lifetime of the suite in hours."""
599 return self._timeout
600
601
Dan Shia25e0d42015-07-23 15:00:04 -0700602 @property
603 def cros_build_spec(self):
604 """The build spec of ChromeOS to test with a firmware build."""
605 return self._cros_build_spec
606
607
608 @property
609 def firmware_rw_build_spec(self):
Dan Shib49bb8b2016-03-01 15:29:27 -0800610 """The build spec of RW firmware to test with a ChromeOS build.
611
612 The value can be firmware or cros.
613 """
Dan Shia25e0d42015-07-23 15:00:04 -0700614 return self._firmware_rw_build_spec
615
616
617 @property
Dan Shib49bb8b2016-03-01 15:29:27 -0800618 def firmware_ro_build_spec(self):
619 """The build spec of RO firmware to test with a ChromeOS build.
620
621 The value can be stable, firmware or cros, where stable is the stable
622 firmware build retrieved from stable_version table.
623 """
624 return self._firmware_ro_build_spec
625
626
627 @property
Dan Shia25e0d42015-07-23 15:00:04 -0700628 def test_source(self):
Dan Shib49bb8b2016-03-01 15:29:27 -0800629 """Source of the test code, value can be `firmware_rw`, `firmware_ro` or
630 `cros`."""
Dan Shia25e0d42015-07-23 15:00:04 -0700631 return self._test_source
632
633
Dan Shi2121a332016-02-25 14:22:22 -0800634 @property
635 def hour(self):
636 """An integer specifying the hour that a nightly run should be triggered
637 """
638 return self._hour
639
640
641 @property
642 def day(self):
643 """An integer specifying the day of a week that a weekly run should be
644 triggered"""
645 return self._day
646
647
648 @property
649 def os_type(self):
650 """Type of OS, e.g., cros, brillo, android."""
651 return self._os_type
652
653
654 @property
655 def launch_control_branches(self):
656 """A list of Launch Control builds."""
657 return self._launch_control_branches
658
659
660 @property
661 def launch_control_targets(self):
662 """A list of Launch Control targets."""
663 return self._launch_control_targets
664
665
Chris Masonefad911a2012-03-29 12:30:26 -0700666 def __str__(self):
667 return self._str
668
669
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700670 def __repr__(self):
671 return self._str
672
673
Chris Masonefad911a2012-03-29 12:30:26 -0700674 def __lt__(self, other):
675 return str(self) < str(other)
676
677
678 def __le__(self, other):
679 return str(self) <= str(other)
680
681
682 def __eq__(self, other):
683 return str(self) == str(other)
684
685
686 def __ne__(self, other):
687 return str(self) != str(other)
688
689
690 def __gt__(self, other):
691 return str(self) > str(other)
692
693
694 def __ge__(self, other):
695 return str(self) >= str(other)
696
697
698 def __hash__(self):
699 """Allows instances to be correctly deduped when used in a set."""
700 return hash(str(self))
701
702
Dan Shia25e0d42015-07-23 15:00:04 -0700703 def _GetCrOSBuild(self, mv, board):
704 """Get the ChromeOS build name to test with firmware build.
705
706 The ChromeOS build to be used is determined by `self.cros_build_spec`.
707 Its value can be:
708 tot: use the latest ToT build.
709 tot-x: use the latest build in x milestone before ToT.
710 Rxx: use the latest build on xx milestone.
711
712 @param board: the board against which to run self._suite.
713 @param mv: an instance of manifest_versions.ManifestVersions.
714
715 @return: The ChromeOS build name to test with firmware build.
716
717 """
718 if not self.cros_build_spec:
719 return None
720 if self.cros_build_spec.startswith('tot'):
721 milestone = TotMilestoneManager().ConvertTotSpec(
722 self.cros_build_spec)[1:]
723 elif self.cros_build_spec.startswith('R'):
724 milestone = self.cros_build_spec[1:]
725 milestone, latest_manifest = mv.GetLatestManifest(
726 board, 'release', milestone=milestone)
727 latest_build = base_event.BuildName(board, 'release', milestone,
728 latest_manifest)
729 logging.debug('Found latest build of %s for spec %s: %s',
730 board, self.cros_build_spec, latest_build)
731 return latest_build
732
733
Dan Shib49bb8b2016-03-01 15:29:27 -0800734 def _GetFirmwareBuild(self, spec, mv, board):
735 """Get the firmware build name to test with ChromeOS build.
Dan Shia25e0d42015-07-23 15:00:04 -0700736
Dan Shi648ce922016-03-26 00:22:47 -0700737 @param spec: build spec for RO or RW firmware, e.g., firmware, cros.
738 For RO firmware, the value can also be in the format of
739 released_ro_X, where X is the index of the list or RO builds
740 defined in global config RELEASED_RO_BUILDS_[board].
741 For example, for spec `released_ro_2`, and global config
742 CROS/RELEASED_RO_BUILDS_veyron_jerry: build1,build2
743 the return firmare RO build should be build2.
Dan Shia25e0d42015-07-23 15:00:04 -0700744 @param mv: an instance of manifest_versions.ManifestVersions.
745 @param board: the board against which to run self._suite.
Dan Shia25e0d42015-07-23 15:00:04 -0700746
Dan Shib49bb8b2016-03-01 15:29:27 -0800747 @return: The firmware build name to test with ChromeOS build.
Dan Shia25e0d42015-07-23 15:00:04 -0700748 """
Dan Shib49bb8b2016-03-01 15:29:27 -0800749 if spec == 'stable':
750 # TODO(crbug.com/577316): Query stable RO firmware.
751 raise NotImplementedError('`stable` RO firmware build is not '
752 'supported yet.')
753 if not spec:
Dan Shia25e0d42015-07-23 15:00:04 -0700754 return None
Dan Shi648ce922016-03-26 00:22:47 -0700755
756 if spec.startswith('released_ro_'):
757 index = int(spec[12:])
758 released_ro_builds = CONFIG.get_config_value(
759 'CROS', 'RELEASED_RO_BUILDS_%s' % board, type=str,
760 default='').split(',')
761 if not released_ro_builds or len(released_ro_builds) < index:
762 return None
763 else:
764 return released_ro_builds[index-1]
765
Dan Shib49bb8b2016-03-01 15:29:27 -0800766 # build_type is the build type of the firmware build, e.g., factory,
767 # firmware or release. If spec is set to cros, build type should be
768 # mapped to release.
769 build_type = 'release' if spec == 'cros' else spec
Dan Shia25e0d42015-07-23 15:00:04 -0700770 latest_milestone, latest_manifest = mv.GetLatestManifest(
771 board, build_type)
772 latest_build = base_event.BuildName(board, build_type, latest_milestone,
773 latest_manifest)
774 logging.debug('Found latest firmware build of %s for spec %s: %s',
Dan Shib49bb8b2016-03-01 15:29:27 -0800775 board, spec, latest_build)
Dan Shia25e0d42015-07-23 15:00:04 -0700776 return latest_build
777
778
Alex Miller7ce1b362012-07-10 09:24:10 -0700779 def AvailableHosts(self, scheduler, board):
780 """Query what hosts are able to run a test on a board and pool
781 combination.
Alex Miller511a9e32012-07-03 09:16:47 -0700782
783 @param scheduler: an instance of DedupingScheduler, as defined in
784 deduping_scheduler.py
785 @param board: the board against which one wants to run the test.
Alex Miller7ce1b362012-07-10 09:24:10 -0700786 @return The list of hosts meeting the board and pool requirements,
787 or None if no hosts were found."""
Alex Millerf2b57442013-09-07 18:40:02 -0700788 if self._boards and board not in self._boards:
789 return []
790
Alex Miller511a9e32012-07-03 09:16:47 -0700791 labels = [Labels.BOARD_PREFIX + board]
792 if self._pool:
Chris Masonecd214e02012-07-10 16:22:10 -0700793 labels.append(Labels.POOL_PREFIX + self._pool)
Alex Miller511a9e32012-07-03 09:16:47 -0700794
Prashanth B6de2bde2014-03-25 18:45:02 -0700795 return scheduler.CheckHostsExist(multiple_labels=labels)
Alex Miller511a9e32012-07-03 09:16:47 -0700796
797
Alex Millerd621cf22012-07-11 13:57:10 -0700798 def ShouldHaveAvailableHosts(self):
799 """As a sanity check, return true if we know for certain that
800 we should be able to schedule this test. If we claim this test
801 should be able to run, and it ends up not being scheduled, then
802 a warning will be reported.
803
804 @return True if this test should be able to run, False otherwise.
805 """
806 return self._pool == 'bvt'
807
808
Dan Shi2121a332016-02-25 14:22:22 -0800809 def _ScheduleSuite(self, scheduler, cros_build, firmware_rw_build,
Dan Shib49bb8b2016-03-01 15:29:27 -0800810 firmware_ro_build, test_source_build,
811 launch_control_build, board, force, run_prod_code=False):
Dan Shi2121a332016-02-25 14:22:22 -0800812 """Try to schedule a suite with given build and board information.
813
814 @param scheduler: an instance of DedupingScheduler, as defined in
815 deduping_scheduler.py
816 @oaran build: Build to run suite for, e.g., 'daisy-release/R18-1655.0.0'
817 and 'git_mnc_release/shamu-eng/123'.
818 @param firmware_rw_build: Firmware RW build to run test with.
Dan Shib49bb8b2016-03-01 15:29:27 -0800819 @param firmware_ro_build: Firmware RO build to run test with.
Dan Shi2121a332016-02-25 14:22:22 -0800820 @param test_source_build: Test source build, used for server-side
821 packaging.
822 @param launch_control_build: Name of a Launch Control build, e.g.,
823 'git_mnc_release/shamu-eng/123'
824 @param board: the board against which to run self._suite.
825 @param force: Always schedule the suite.
826 @param run_prod_code: If True, the suite will run the test code that
827 lives in prod aka the test code currently on the
828 lab servers. If False, the control files and test
829 code for this suite run will be retrieved from the
830 build artifacts. Default is False.
831 """
832 test_source_build_msg = (
833 ' Test source build is %s.' % test_source_build
Dan Shi8446fac2016-03-02 22:07:39 -0800834 if test_source_build else '')
Dan Shi2121a332016-02-25 14:22:22 -0800835 firmware_rw_build_msg = (
836 ' Firmware RW build is %s.' % firmware_rw_build
Dan Shi8446fac2016-03-02 22:07:39 -0800837 if firmware_rw_build else '')
Dan Shib49bb8b2016-03-01 15:29:27 -0800838 firmware_ro_build_msg = (
839 ' Firmware RO build is %s.' % firmware_ro_build
Dan Shi8446fac2016-03-02 22:07:39 -0800840 if firmware_ro_build else '')
Dan Shi2121a332016-02-25 14:22:22 -0800841 build_string = cros_build or launch_control_build
Dan Shib49bb8b2016-03-01 15:29:27 -0800842 logging.debug('Schedule %s for build %s.%s%s%s',
Dan Shi2121a332016-02-25 14:22:22 -0800843 self._suite, build_string, test_source_build_msg,
Dan Shib49bb8b2016-03-01 15:29:27 -0800844 firmware_rw_build_msg, firmware_ro_build_msg)
Dan Shi2121a332016-02-25 14:22:22 -0800845
846 if not scheduler.ScheduleSuite(
847 self._suite, board, cros_build, self._pool, self._num,
848 self._priority, self._timeout, force,
849 file_bugs=self._file_bugs,
850 firmware_rw_build=firmware_rw_build,
Dan Shib49bb8b2016-03-01 15:29:27 -0800851 firmware_ro_build=firmware_ro_build,
Dan Shi2121a332016-02-25 14:22:22 -0800852 test_source_build=test_source_build,
853 job_retry=self._job_retry,
854 launch_control_build=launch_control_build,
855 run_prod_code=run_prod_code):
856 logging.info('Skipping scheduling %s on %s for %s',
857 self._suite, build_string, board)
858
859
860 def _Run_CrOS_Builds(self, scheduler, branch_builds, board, force=False,
861 mv=None):
862 """Run this task for CrOS builds. Returns False if it should be
863 destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -0700864
Chris Masone013859b2012-04-01 13:45:26 -0700865 Execute this task. Attempt to schedule the associated suite.
866 Return True if this task should be kept around, False if it
867 should be destroyed. This allows for one-shot Tasks.
Chris Masonefad911a2012-03-29 12:30:26 -0700868
869 @param scheduler: an instance of DedupingScheduler, as defined in
870 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -0700871 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -0700872 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -0700873 {'R18': ['x86-alex-release/R18-1655.0.0'],
874 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -0700875 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -0700876 @param force: Always schedule the suite.
Dan Shia25e0d42015-07-23 15:00:04 -0700877 @param mv: an instance of manifest_versions.ManifestVersions.
878
Chris Masone013859b2012-04-01 13:45:26 -0700879 @return True if the task should be kept, False if not
Dan Shia25e0d42015-07-23 15:00:04 -0700880
Chris Masonefad911a2012-03-29 12:30:26 -0700881 """
Chris Masonea3a38172012-05-14 15:19:56 -0700882 logging.info('Running %s on %s', self._name, board)
Dan Shia25e0d42015-07-23 15:00:04 -0700883 is_firmware_build = 'firmware' in self.branch_specs
Dan Shib49bb8b2016-03-01 15:29:27 -0800884
885 # firmware_xx_build is only needed if firmware_xx_build_spec is given.
Dan Shia25e0d42015-07-23 15:00:04 -0700886 firmware_rw_build = None
Dan Shib49bb8b2016-03-01 15:29:27 -0800887 firmware_ro_build = None
Dan Shia25e0d42015-07-23 15:00:04 -0700888 try:
889 if is_firmware_build:
890 # When build specified in branch_specs is a firmware build,
891 # we need a ChromeOS build to test with the firmware build.
892 cros_build = self._GetCrOSBuild(mv, board)
Dan Shib49bb8b2016-03-01 15:29:27 -0800893 elif self.firmware_rw_build_spec or self.firmware_ro_build_spec:
894 # When firmware_xx_build_spec is specified, the test involves
895 # updating the RW firmware by firmware build specified in
896 # firmware_xx_build_spec.
897 firmware_rw_build = self._GetFirmwareBuild(
898 self.firmware_rw_build_spec, mv, board)
899 firmware_ro_build = self._GetFirmwareBuild(
900 self.firmware_ro_build_spec, mv, board)
Dan Shi648ce922016-03-26 00:22:47 -0700901 # If RO firmware is specified, force to create suite, because
902 # dedupe based on test source build does not reflect the change
903 # of RO firmware.
904 if firmware_ro_build:
905 force = True
Dan Shia25e0d42015-07-23 15:00:04 -0700906 except manifest_versions.QueryException as e:
907 logging.error(e)
908 logging.error('Running %s on %s is failed. Failed to find build '
909 'required to run the suite.', self._name, board)
910 return False
911
Dan Shi648ce922016-03-26 00:22:47 -0700912 # Return if there is no firmware RO build found for given spec.
913 if not firmware_ro_build and self.firmware_ro_build_spec:
914 return True
915
Chris Masone96f16632012-04-04 18:36:03 -0700916 builds = []
Chris Masonefe5a5092012-04-11 18:29:07 -0700917 for branch, build in branch_builds.iteritems():
Chris Masonea3a38172012-05-14 15:19:56 -0700918 logging.info('Checking if %s fits spec %r',
919 branch, self.branch_specs)
Chris Masone96f16632012-04-04 18:36:03 -0700920 if self._FitsSpec(branch):
Dan Shibde10772015-08-18 10:15:58 -0700921 logging.debug('Build %s fits the spec.', build)
Chris Masone05b19442012-04-17 13:37:55 -0700922 builds.extend(build)
Chris Masone96f16632012-04-04 18:36:03 -0700923 for build in builds:
Chris Masone3fba86f2012-04-03 10:06:56 -0700924 try:
Dan Shia25e0d42015-07-23 15:00:04 -0700925 if is_firmware_build:
926 firmware_rw_build = build
927 else:
928 cros_build = build
929 if self.test_source == Builds.FIRMWARE_RW:
930 test_source_build = firmware_rw_build
931 elif self.test_source == Builds.CROS:
932 test_source_build = cros_build
933 else:
934 test_source_build = None
Dan Shi2121a332016-02-25 14:22:22 -0800935 self._ScheduleSuite(scheduler, cros_build, firmware_rw_build,
Dan Shib49bb8b2016-03-01 15:29:27 -0800936 firmware_ro_build, test_source_build,
937 None, board, force)
Chris Masone3fba86f2012-04-03 10:06:56 -0700938 except deduping_scheduler.DedupingSchedulerException as e:
939 logging.error(e)
Chris Masonefad911a2012-03-29 12:30:26 -0700940 return True
941
942
Dan Shi2121a332016-02-25 14:22:22 -0800943 def _Run_LaunchControl_Builds(self, scheduler, launch_control_builds, board,
944 force=False):
945 """Run this task. Returns False if it should be destroyed.
946
947 Execute this task. Attempt to schedule the associated suite.
948 Return True if this task should be kept around, False if it
949 should be destroyed. This allows for one-shot Tasks.
950
951 @param scheduler: an instance of DedupingScheduler, as defined in
952 deduping_scheduler.py
953 @param launch_control_builds: A list of Launch Control builds.
954 @param board: the board against which to run self._suite.
955 @param force: Always schedule the suite.
956
957 @return True if the task should be kept, False if not
958
959 """
960 logging.info('Running %s on %s', self._name, board)
961 for build in launch_control_builds:
962 try:
Dan Shi8446fac2016-03-02 22:07:39 -0800963 self._ScheduleSuite(scheduler, None, None, None,
964 test_source_build=build,
Dan Shi2121a332016-02-25 14:22:22 -0800965 launch_control_build=build, board=board,
966 force=force, run_prod_code=True)
967 except deduping_scheduler.DedupingSchedulerException as e:
968 logging.error(e)
969 return True
970
971
972 def Run(self, scheduler, branch_builds, board, force=False, mv=None,
973 launch_control_builds=None):
974 """Run this task. Returns False if it should be destroyed.
975
976 Execute this task. Attempt to schedule the associated suite.
977 Return True if this task should be kept around, False if it
978 should be destroyed. This allows for one-shot Tasks.
979
980 @param scheduler: an instance of DedupingScheduler, as defined in
981 deduping_scheduler.py
982 @param branch_builds: a dict mapping branch name to the build(s) to
983 install for that branch, e.g.
984 {'R18': ['x86-alex-release/R18-1655.0.0'],
985 'R19': ['x86-alex-release/R19-2077.0.0']}
986 @param board: the board against which to run self._suite.
987 @param force: Always schedule the suite.
988 @param mv: an instance of manifest_versions.ManifestVersions.
989 @param launch_control_builds: A list of Launch Control builds.
990
991 @return True if the task should be kept, False if not
992
993 """
994 if ((self._os_type == OS_TYPE_CROS and not branch_builds) or
995 (self._os_type != OS_TYPE_CROS and not launch_control_builds)):
996 logging.debug('No build to run, skip running %s on %s.', self._name,
997 board)
998 # Return True so the task will be kept, as the given build and board
999 # do not match.
1000 return True
1001
1002 if self._os_type == OS_TYPE_CROS:
1003 return self._Run_CrOS_Builds(
1004 scheduler, branch_builds, board, force, mv)
1005 else:
1006 return self._Run_LaunchControl_Builds(
1007 scheduler, launch_control_builds, board, force)
1008
1009
Chris Masone013859b2012-04-01 13:45:26 -07001010class OneShotTask(Task):
1011 """A Task that can be run only once. Can schedule itself."""
Chris Masonefad911a2012-03-29 12:30:26 -07001012
1013
Dan Shi2121a332016-02-25 14:22:22 -08001014 def Run(self, scheduler, branch_builds, board, force=False, mv=None,
1015 launch_control_builds=None):
Chris Masone013859b2012-04-01 13:45:26 -07001016 """Run this task. Returns False, indicating it should be destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -07001017
Chris Masone013859b2012-04-01 13:45:26 -07001018 Run this task. Attempt to schedule the associated suite.
1019 Return False, indicating to the caller that it should discard this task.
Chris Masonefad911a2012-03-29 12:30:26 -07001020
1021 @param scheduler: an instance of DedupingScheduler, as defined in
1022 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -07001023 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -07001024 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -07001025 {'R18': ['x86-alex-release/R18-1655.0.0'],
1026 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -07001027 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -07001028 @param force: Always schedule the suite.
Dan Shia25e0d42015-07-23 15:00:04 -07001029 @param mv: an instance of manifest_versions.ManifestVersions.
Dan Shi2121a332016-02-25 14:22:22 -08001030 @param launch_control_builds: A list of Launch Control builds.
Dan Shia25e0d42015-07-23 15:00:04 -07001031
Chris Masonefad911a2012-03-29 12:30:26 -07001032 @return False
Dan Shia25e0d42015-07-23 15:00:04 -07001033
Chris Masonefad911a2012-03-29 12:30:26 -07001034 """
Dan Shia25e0d42015-07-23 15:00:04 -07001035 super(OneShotTask, self).Run(scheduler, branch_builds, board, force,
Dan Shi2121a332016-02-25 14:22:22 -08001036 mv, launch_control_builds)
Chris Masonefad911a2012-03-29 12:30:26 -07001037 return False