blob: 94dc571cde69bbd82c8dffd2a5923b77a1ede4cc [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
Dan Shi84585362016-10-03 14:11:43 -070035# regex to parse the dut count from board label. Note that the regex makes sure
36# there is only one board specified in `boards`
37TESTBED_DUT_COUNT_REGEX = '[^,]*-(\d+)'
38
Chris Masone96f16632012-04-04 18:36:03 -070039class MalformedConfigEntry(Exception):
40 """Raised to indicate a failure to parse a Task out of a config."""
41 pass
Chris Masonefad911a2012-03-29 12:30:26 -070042
43
Alex Miller0d003572013-03-18 11:51:30 -070044BARE_BRANCHES = ['factory', 'firmware']
Chris Masone67f06d62012-04-12 15:16:56 -070045
46
47def PickBranchName(type, milestone):
Dan Shiaceb91d2013-02-20 12:41:28 -080048 """Pick branch name. If type is among BARE_BRANCHES, return type,
49 otherwise, return milestone.
50
51 @param type: type of the branch, e.g., 'release', 'factory', or 'firmware'
52 @param milestone: CrOS milestone number
53 """
Chris Masone67f06d62012-04-12 15:16:56 -070054 if type in BARE_BRANCHES:
55 return type
56 return milestone
57
58
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070059class TotMilestoneManager(object):
60 """A class capable of converting tot string to milestone numbers.
61
62 This class is used as a cache for the tot milestone, so we don't
63 repeatedly hit google storage for all O(100) tasks in suite
64 scheduler's ini file.
65 """
66
Fang Dengf08814a2015-08-03 18:12:18 +000067 __metaclass__ = server_utils.Singleton
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070068
Dan Shi23245142015-01-22 13:22:28 -080069 # True if suite_scheduler is running for sanity check. When it's set to
70 # True, the code won't make gsutil call to get the actual tot milestone to
71 # avoid dependency on the installation of gsutil to run sanity check.
72 is_sanity = False
73
74
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070075 @staticmethod
76 def _tot_milestone():
77 """Get the tot milestone, eg: R40
78
79 @returns: A string representing the Tot milestone as declared by
80 the LATEST_BUILD_URL, or an empty string if LATEST_BUILD_URL
81 doesn't exist.
82 """
Dan Shi23245142015-01-22 13:22:28 -080083 if TotMilestoneManager.is_sanity:
84 logging.info('suite_scheduler is running for sanity purpose, no '
85 'need to get the actual tot milestone string.')
86 return 'R40'
87
Prashanth Balasubramanian1b859622014-10-28 16:02:15 -070088 cmd = ['gsutil', 'cat', constants.LATEST_BUILD_URL]
89 proc = subprocess.Popen(
90 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
91 stdout, stderr = proc.communicate()
92 if proc.poll():
93 logging.warning('Failed to get latest build: %s', stderr)
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070094 return ''
Prashanth Balasubramanian1b859622014-10-28 16:02:15 -070095 return stdout.split('-')[0]
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070096
97
98 def refresh(self):
99 """Refresh the tot milestone string managed by this class."""
100 self.tot = self._tot_milestone()
101
102
103 def __init__(self):
104 """Initialize a TotMilestoneManager."""
105 self.refresh()
106
107
108 def ConvertTotSpec(self, tot_spec):
109 """Converts a tot spec to the appropriate milestone.
110
111 Assume tot is R40:
112 tot -> R40
113 tot-1 -> R39
114 tot-2 -> R38
115 tot-(any other numbers) -> R40
116
117 With the last option one assumes that a malformed configuration that has
118 'tot' in it, wants at least tot.
119
120 @param tot_spec: A string representing the tot spec.
121 @raises MalformedConfigEntry: If the tot_spec doesn't match the
122 expected format.
123 """
124 tot_spec = tot_spec.lower()
125 match = re.match('(tot)[-]?(1$|2$)?', tot_spec)
126 if not match:
127 raise MalformedConfigEntry(
128 "%s isn't a valid branch spec." % tot_spec)
129 tot_mstone = self.tot
130 num_back = match.groups()[1]
131 if num_back:
132 tot_mstone_num = tot_mstone.lstrip('R')
133 tot_mstone = tot_mstone.replace(
134 tot_mstone_num, str(int(tot_mstone_num)-int(num_back)))
135 return tot_mstone
136
137
Chris Masone013859b2012-04-01 13:45:26 -0700138class Task(object):
Chris Masonefad911a2012-03-29 12:30:26 -0700139 """Represents an entry from the scheduler config. Can schedule itself.
140
141 Each entry from the scheduler config file maps one-to-one to a
Chris Masone013859b2012-04-01 13:45:26 -0700142 Task. Each instance has enough info to schedule itself
Chris Masonefad911a2012-03-29 12:30:26 -0700143 on-demand with the AFE.
144
Aviv Keshet52c7a212015-12-07 15:27:22 -0800145 This class also overrides __hash__() and all comparator methods to enable
Chris Masonefad911a2012-03-29 12:30:26 -0700146 correct use in dicts, sets, etc.
147 """
148
Chris Masone96f16632012-04-04 18:36:03 -0700149
150 @staticmethod
151 def CreateFromConfigSection(config, section):
152 """Create a Task from a section of a config file.
153
154 The section to parse should look like this:
155 [TaskName]
156 suite: suite_to_run # Required
157 run_on: event_on which to run # Required
Dan Shi9f256d92016-01-22 00:09:25 -0800158 hour: integer of the hour to run, only applies to nightly. # Optional
Dan Shiaceb91d2013-02-20 12:41:28 -0800159 branch_specs: factory,firmware,>=R12 or ==R12 # Optional
Chris Masone96f16632012-04-04 18:36:03 -0700160 pool: pool_of_devices # Optional
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700161 num: sharding_factor # int, Optional
Alex Millerf2b57442013-09-07 18:40:02 -0700162 boards: board1, board2 # comma seperated string, Optional
Dan Shi2121a332016-02-25 14:22:22 -0800163 # Settings for Launch Control builds only:
164 os_type: brillo # Type of OS, e.g., cros, brillo, android. Default is
165 cros. Required for android/brillo builds.
166 branches: git_mnc_release # comma separated string of Launch Control
167 branches. Required and only applicable for android/brillo
168 builds.
169 targets: dragonboard-eng # comma separated string of build targets.
170 Required and only applicable for android/brillo builds.
Dan Shi84585362016-10-03 14:11:43 -0700171 testbed_dut_count: Number of duts to test when using a testbed.
Chris Masone96f16632012-04-04 18:36:03 -0700172
Chris Masone67f06d62012-04-12 15:16:56 -0700173 By default, Tasks run on all release branches, not factory or firmware.
174
Chris Masone96f16632012-04-04 18:36:03 -0700175 @param config: a ForgivingConfigParser.
176 @param section: the section to parse into a Task.
177 @return keyword, Task object pair. One or both will be None on error.
178 @raise MalformedConfigEntry if there's a problem parsing |section|.
179 """
Alex Miller06695022012-07-18 09:31:36 -0700180 if not config.has_section(section):
181 raise MalformedConfigEntry('unknown section %s' % section)
182
Alex Millerf2b57442013-09-07 18:40:02 -0700183 allowed = set(['suite', 'run_on', 'branch_specs', 'pool', 'num',
Dan Shia25e0d42015-07-23 15:00:04 -0700184 'boards', 'file_bugs', 'cros_build_spec',
Dan Shib49bb8b2016-03-01 15:29:27 -0800185 'firmware_rw_build_spec', 'firmware_ro_build_spec',
186 'test_source', 'job_retry', 'hour', 'day', 'branches',
Dan Shi1d0e6392016-10-26 13:02:37 -0700187 'targets', 'os_type', 'no_delay'])
Alex Millerbb535e22012-07-11 20:11:33 -0700188 # The parameter of union() is the keys under the section in the config
189 # The union merges this with the allowed set, so if any optional keys
190 # are omitted, then they're filled in. If any extra keys are present,
191 # then they will expand unioned set, causing it to fail the following
192 # comparison against the allowed set.
193 section_headers = allowed.union(dict(config.items(section)).keys())
194 if allowed != section_headers:
195 raise MalformedConfigEntry('unknown entries: %s' %
196 ", ".join(map(str, section_headers.difference(allowed))))
197
Chris Masone96f16632012-04-04 18:36:03 -0700198 keyword = config.getstring(section, 'run_on')
Dan Shi9f256d92016-01-22 00:09:25 -0800199 hour = config.getstring(section, 'hour')
Chris Masone96f16632012-04-04 18:36:03 -0700200 suite = config.getstring(section, 'suite')
Dan Shi2121a332016-02-25 14:22:22 -0800201 branch_specs = config.getstring(section, 'branch_specs')
Chris Masone96f16632012-04-04 18:36:03 -0700202 pool = config.getstring(section, 'pool')
Alex Millerf2b57442013-09-07 18:40:02 -0700203 boards = config.getstring(section, 'boards')
Prashanth B6de2bde2014-03-25 18:45:02 -0700204 file_bugs = config.getboolean(section, 'file_bugs')
Dan Shia25e0d42015-07-23 15:00:04 -0700205 cros_build_spec = config.getstring(section, 'cros_build_spec')
206 firmware_rw_build_spec = config.getstring(
207 section, 'firmware_rw_build_spec')
Dan Shib49bb8b2016-03-01 15:29:27 -0800208 firmware_ro_build_spec = config.getstring(
209 section, 'firmware_ro_build_spec')
Dan Shia25e0d42015-07-23 15:00:04 -0700210 test_source = config.getstring(section, 'test_source')
Dan Shi29a16992015-09-22 11:29:58 -0700211 job_retry = config.getboolean(section, 'job_retry')
Dan Shi1d0e6392016-10-26 13:02:37 -0700212 no_delay = config.getboolean(section, 'no_delay')
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700213 for klass in driver.Driver.EVENT_CLASSES:
214 if klass.KEYWORD == keyword:
215 priority = klass.PRIORITY
216 timeout = klass.TIMEOUT
217 break
218 else:
219 priority = None
220 timeout = None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700221 try:
222 num = config.getint(section, 'num')
223 except ValueError as e:
Dan Shi9f256d92016-01-22 00:09:25 -0800224 raise MalformedConfigEntry("Ill-specified 'num': %r" % e)
Chris Masone96f16632012-04-04 18:36:03 -0700225 if not keyword:
226 raise MalformedConfigEntry('No event to |run_on|.')
227 if not suite:
228 raise MalformedConfigEntry('No |suite|')
Dan Shi9f256d92016-01-22 00:09:25 -0800229 try:
230 hour = config.getint(section, 'hour')
231 except ValueError as e:
232 raise MalformedConfigEntry("Ill-specified 'hour': %r" % e)
233 if hour is not None and (hour < 0 or hour > 23):
234 raise MalformedConfigEntry(
235 '`hour` must be an integer between 0 and 23.')
236 if hour is not None and keyword != 'nightly':
237 raise MalformedConfigEntry(
238 '`hour` is the trigger time that can only apply to nightly '
239 'event.')
Dan Shice1f20a2016-01-25 17:27:40 -0800240
Dan Shi84585362016-10-03 14:11:43 -0700241 testbed_dut_count = None
Dan Shieb014bc2016-10-10 10:20:57 -0700242 if boards:
243 match = re.match(TESTBED_DUT_COUNT_REGEX, boards)
244 if match:
245 testbed_dut_count = int(match.group(1))
Dan Shi84585362016-10-03 14:11:43 -0700246
Dan Shice1f20a2016-01-25 17:27:40 -0800247 try:
248 day = config.getint(section, 'day')
249 except ValueError as e:
250 raise MalformedConfigEntry("Ill-specified 'day': %r" % e)
251 if day is not None and (day < 0 or day > 6):
252 raise MalformedConfigEntry(
253 '`day` must be an integer between 0 and 6, where 0 is for '
254 'Monday and 6 is for Sunday.')
255 if day is not None and keyword != 'weekly':
256 raise MalformedConfigEntry(
257 '`day` is the trigger of the day of a week, that can only '
258 'apply to weekly events.')
259
Chris Masone96f16632012-04-04 18:36:03 -0700260 specs = []
Dan Shi2121a332016-02-25 14:22:22 -0800261 if branch_specs:
262 specs = re.split('\s*,\s*', branch_specs)
Chris Masone96f16632012-04-04 18:36:03 -0700263 Task.CheckBranchSpecs(specs)
Dan Shi2121a332016-02-25 14:22:22 -0800264
265 os_type = config.getstring(section, 'os_type') or OS_TYPE_CROS
266 if os_type not in OS_TYPES:
267 raise MalformedConfigEntry('`os_type` must be one of %s' % OS_TYPES)
268
269 lc_branches = config.getstring(section, 'branches')
270 lc_targets = config.getstring(section, 'targets')
271 if os_type == OS_TYPE_CROS and (lc_branches or lc_targets):
272 raise MalformedConfigEntry(
273 '`branches` and `targets` are only supported for Launch '
274 'Control builds, not ChromeOS builds.')
275 if (os_type in OS_TYPES_LAUNCH_CONTROL and
276 (not lc_branches or not lc_targets)):
277 raise MalformedConfigEntry(
278 '`branches` and `targets` must be specified for Launch '
279 'Control builds.')
Dan Shi84585362016-10-03 14:11:43 -0700280 if (os_type in OS_TYPES_LAUNCH_CONTROL and boards and
281 not testbed_dut_count):
Dan Shi2121a332016-02-25 14:22:22 -0800282 raise MalformedConfigEntry(
283 '`boards` for Launch Control builds are retrieved from '
284 '`targets` setting, it should not be set for Launch '
285 'Control builds.')
Dan Shi84585362016-10-03 14:11:43 -0700286 if os_type == OS_TYPE_CROS and testbed_dut_count:
287 raise MalformedConfigEntry(
288 'testbed_dut_count is only supported for Launch Control '
289 'builds testing with testbed.')
Dan Shi2121a332016-02-25 14:22:22 -0800290
291 # Extract boards from targets list.
292 if os_type in OS_TYPES_LAUNCH_CONTROL:
293 boards = ''
294 for target in lc_targets.split(','):
295 board_name, _ = server_utils.parse_launch_control_target(
296 target.strip())
Dan Shi43274402016-11-04 15:13:43 -0700297 # Translate board name in build target to the actual board name.
298 board_name = server_utils.ANDROID_TARGET_TO_BOARD_MAP.get(
299 board_name, board_name)
Dan Shi8db7e612016-07-21 12:55:16 -0700300 boards += '%s,' % board_name
Dan Shi2121a332016-02-25 14:22:22 -0800301 boards = boards.strip(',')
302
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700303 return keyword, Task(section, suite, specs, pool, num, boards,
Prashanth B6de2bde2014-03-25 18:45:02 -0700304 priority, timeout,
Dan Shia25e0d42015-07-23 15:00:04 -0700305 file_bugs=file_bugs if file_bugs else False,
306 cros_build_spec=cros_build_spec,
307 firmware_rw_build_spec=firmware_rw_build_spec,
Dan Shib49bb8b2016-03-01 15:29:27 -0800308 firmware_ro_build_spec=firmware_ro_build_spec,
Dan Shi9f256d92016-01-22 00:09:25 -0800309 test_source=test_source, job_retry=job_retry,
Dan Shi2121a332016-02-25 14:22:22 -0800310 hour=hour, day=day, os_type=os_type,
311 launch_control_branches=lc_branches,
Dan Shi84585362016-10-03 14:11:43 -0700312 launch_control_targets=lc_targets,
Dan Shi1d0e6392016-10-26 13:02:37 -0700313 testbed_dut_count=testbed_dut_count,
314 no_delay=no_delay)
Chris Masone96f16632012-04-04 18:36:03 -0700315
316
317 @staticmethod
318 def CheckBranchSpecs(branch_specs):
319 """Make sure entries in the list branch_specs are correctly formed.
320
Chris Masone67f06d62012-04-12 15:16:56 -0700321 We accept any of BARE_BRANCHES in |branch_specs|, as
Dan Shiaceb91d2013-02-20 12:41:28 -0800322 well as _one_ string of the form '>=RXX' or '==RXX', where 'RXX' is a
Chris Masone96f16632012-04-04 18:36:03 -0700323 CrOS milestone number.
324
325 @param branch_specs: an iterable of branch specifiers.
326 @raise MalformedConfigEntry if there's a problem parsing |branch_specs|.
327 """
328 have_seen_numeric_constraint = False
329 for branch in branch_specs:
Chris Masone67f06d62012-04-12 15:16:56 -0700330 if branch in BARE_BRANCHES:
Chris Masone96f16632012-04-04 18:36:03 -0700331 continue
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700332 if not have_seen_numeric_constraint:
333 #TODO(beeps): Why was <= dropped on the floor?
334 if branch.startswith('>=R') or branch.startswith('==R'):
335 have_seen_numeric_constraint = True
336 elif 'tot' in branch:
337 TotMilestoneManager().ConvertTotSpec(
338 branch[branch.index('tot'):])
339 have_seen_numeric_constraint = True
Chris Masone96f16632012-04-04 18:36:03 -0700340 continue
Chris Masone97cf0a72012-05-16 09:55:52 -0700341 raise MalformedConfigEntry("%s isn't a valid branch spec." % branch)
Chris Masone96f16632012-04-04 18:36:03 -0700342
343
Alex Millerf2b57442013-09-07 18:40:02 -0700344 def __init__(self, name, suite, branch_specs, pool=None, num=None,
Dan Shia25e0d42015-07-23 15:00:04 -0700345 boards=None, priority=None, timeout=None, file_bugs=False,
346 cros_build_spec=None, firmware_rw_build_spec=None,
Dan Shib49bb8b2016-03-01 15:29:27 -0800347 firmware_ro_build_spec=None, test_source=None, job_retry=False,
348 hour=None, day=None, os_type=OS_TYPE_CROS,
Dan Shi84585362016-10-03 14:11:43 -0700349 launch_control_branches=None, launch_control_targets=None,
Dan Shi1d0e6392016-10-26 13:02:37 -0700350 testbed_dut_count=None, no_delay=False):
Chris Masonefad911a2012-03-29 12:30:26 -0700351 """Constructor
352
Chris Masone96f16632012-04-04 18:36:03 -0700353 Given an iterable in |branch_specs|, pre-vetted using CheckBranchSpecs,
354 we'll store them such that _FitsSpec() can be used to check whether a
355 given branch 'fits' with the specifications passed in here.
356 For example, given branch_specs = ['factory', '>=R18'], we'd set things
357 up so that _FitsSpec() would return True for 'factory', or 'RXX'
Dan Shiaceb91d2013-02-20 12:41:28 -0800358 where XX is a number >= 18. Same check is done for branch_specs = [
359 'factory', '==R18'], which limit the test to only one specific branch.
Chris Masone96f16632012-04-04 18:36:03 -0700360
361 Given branch_specs = ['factory', 'firmware'], _FitsSpec()
362 would pass only those two specific strings.
363
364 Example usage:
Chris Masonecc4631d2012-04-20 12:06:39 -0700365 t = Task('Name', 'suite', ['factory', '>=R18'])
Chris Masone96f16632012-04-04 18:36:03 -0700366 t._FitsSpec('factory') # True
367 t._FitsSpec('R19') # True
368 t._FitsSpec('R17') # False
369 t._FitsSpec('firmware') # False
370 t._FitsSpec('goober') # False
371
Dan Shiaceb91d2013-02-20 12:41:28 -0800372 t = Task('Name', 'suite', ['factory', '==R18'])
373 t._FitsSpec('R19') # False, branch does not equal to 18
374 t._FitsSpec('R18') # True
375 t._FitsSpec('R17') # False
376
Dan Shia25e0d42015-07-23 15:00:04 -0700377 cros_build_spec and firmware_rw_build_spec are set for tests require
378 firmware update on the dut. Only one of them can be set.
379 For example:
380 branch_specs: ==tot
381 firmware_rw_build_spec: firmware
382 test_source: cros
383 This will run test using latest build on firmware branch, and the latest
384 ChromeOS build on ToT. The test source build is ChromeOS build.
385
386 branch_specs: firmware
387 cros_build_spec: ==tot-1
388 test_source: firmware_rw
389 This will run test using latest build on firmware branch, and the latest
390 ChromeOS build on dev channel (ToT-1). The test source build is the
391 firmware RW build.
392
Dan Shi59562a82016-01-06 16:05:31 -0800393 branch_specs: ==tot
394 firmware_rw_build_spec: cros
395 test_source: cros
396 This will run test using latest ChromeOS and firmware RW build on ToT.
397 ChromeOS build on ToT. The test source build is ChromeOS build.
398
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700399 @param name: name of this task, e.g. 'NightlyPower'
Chris Masonefad911a2012-03-29 12:30:26 -0700400 @param suite: the name of the suite to run, e.g. 'bvt'
Chris Masone96f16632012-04-04 18:36:03 -0700401 @param branch_specs: a pre-vetted iterable of branch specifiers,
402 e.g. ['>=R18', 'factory']
Chris Masonefad911a2012-03-29 12:30:26 -0700403 @param pool: the pool of machines to use for scheduling purposes.
404 Default: None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700405 @param num: the number of devices across which to shard the test suite.
Aviv Keshetd83ef442013-01-16 16:19:35 -0800406 Type: integer or None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700407 Default: None
Dan Shi9f256d92016-01-22 00:09:25 -0800408 @param boards: A comma separated list of boards to run this task on.
Alex Millerf2b57442013-09-07 18:40:02 -0700409 Default: Run on all boards.
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700410 @param priority: The string name of a priority from
411 client.common_lib.priorities.Priority.
412 @param timeout: The max lifetime of the suite in hours.
Prashanth B6de2bde2014-03-25 18:45:02 -0700413 @param file_bugs: True if bug filing is desired for the suite created
414 for this task.
Dan Shia25e0d42015-07-23 15:00:04 -0700415 @param cros_build_spec: Spec used to determine the ChromeOS build to
416 test with a firmware build, e.g., tot, R41 etc.
Dan Shib49bb8b2016-03-01 15:29:27 -0800417 @param firmware_rw_build_spec: Spec used to determine the firmware RW
418 build test with a ChromeOS build.
419 @param firmware_ro_build_spec: Spec used to determine the firmware RO
420 build test with a ChromeOS build.
Dan Shia25e0d42015-07-23 15:00:04 -0700421 @param test_source: The source of test code when firmware will be
Dan Shib49bb8b2016-03-01 15:29:27 -0800422 updated in the test. The value can be `firmware_rw`,
423 `firmware_ro` or `cros`.
Dan Shi29a16992015-09-22 11:29:58 -0700424 @param job_retry: Set to True to enable job-level retry. Default is
425 False.
Dan Shi9f256d92016-01-22 00:09:25 -0800426 @param hour: An integer specifying the hour that a nightly run should
427 be triggered, default is set to 21.
Dan Shice1f20a2016-01-25 17:27:40 -0800428 @param day: An integer specifying the day of a week that a weekly run
Dan Shi2121a332016-02-25 14:22:22 -0800429 should be triggered, default is set to 5, which is Saturday.
430 @param os_type: Type of OS, e.g., cros, brillo, android. Default is
431 cros. The argument is required for android/brillo builds.
432 @param launch_control_branches: Comma separated string of Launch Control
433 branches. The argument is required and only applicable for
434 android/brillo builds.
435 @param launch_control_targets: Comma separated string of build targets
436 for Launch Control builds. The argument is required and only
437 applicable for android/brillo builds.
Dan Shi84585362016-10-03 14:11:43 -0700438 @param testbed_dut_count: Number of duts to test when using a testbed.
Dan Shi1d0e6392016-10-26 13:02:37 -0700439 @param no_delay: Set to True to allow suite to be created without
440 configuring delay_minutes. Default is False.
Chris Masonefad911a2012-03-29 12:30:26 -0700441 """
Chris Masonecc4631d2012-04-20 12:06:39 -0700442 self._name = name
Chris Masonefad911a2012-03-29 12:30:26 -0700443 self._suite = suite
Chris Masone96f16632012-04-04 18:36:03 -0700444 self._branch_specs = branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700445 self._pool = pool
Aviv Keshetd83ef442013-01-16 16:19:35 -0800446 self._num = num
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700447 self._priority = priority
448 self._timeout = timeout
Prashanth B6de2bde2014-03-25 18:45:02 -0700449 self._file_bugs = file_bugs
Dan Shia25e0d42015-07-23 15:00:04 -0700450 self._cros_build_spec = cros_build_spec
451 self._firmware_rw_build_spec = firmware_rw_build_spec
Dan Shib49bb8b2016-03-01 15:29:27 -0800452 self._firmware_ro_build_spec = firmware_ro_build_spec
Dan Shia25e0d42015-07-23 15:00:04 -0700453 self._test_source = test_source
Dan Shi29a16992015-09-22 11:29:58 -0700454 self._job_retry = job_retry
Dan Shi2121a332016-02-25 14:22:22 -0800455 self._hour = hour
456 self._day = day
457 self._os_type = os_type
458 self._launch_control_branches = (
459 [b.strip() for b in launch_control_branches.split(',')]
460 if launch_control_branches else [])
461 self._launch_control_targets = (
462 [t.strip() for t in launch_control_targets.split(',')]
463 if launch_control_targets else [])
Dan Shi84585362016-10-03 14:11:43 -0700464 self._testbed_dut_count = testbed_dut_count
Dan Shi1d0e6392016-10-26 13:02:37 -0700465 self._no_delay = no_delay
Dan Shia25e0d42015-07-23 15:00:04 -0700466
Dan Shib49bb8b2016-03-01 15:29:27 -0800467 if ((self._firmware_rw_build_spec or self._firmware_ro_build_spec or
468 cros_build_spec) and
469 not self.test_source in [Builds.FIRMWARE_RW, Builds.FIRMWARE_RO,
470 Builds.CROS]):
Dan Shia25e0d42015-07-23 15:00:04 -0700471 raise MalformedConfigEntry(
472 'You must specify the build for test source. It can only '
Dan Shib49bb8b2016-03-01 15:29:27 -0800473 'be `firmware_rw`, `firmware_ro` or `cros`.')
Dan Shia25e0d42015-07-23 15:00:04 -0700474 if self._firmware_rw_build_spec and cros_build_spec:
475 raise MalformedConfigEntry(
476 'You cannot specify both firmware_rw_build_spec and '
477 'cros_build_spec. firmware_rw_build_spec is used to specify'
478 ' a firmware build when the suite requires firmware to be '
Dan Shi59562a82016-01-06 16:05:31 -0800479 'updated in the dut, its value can only be `firmware` or '
480 '`cros`. cros_build_spec is used to specify a ChromeOS '
481 'build when build_specs is set to firmware.')
Dan Shia25e0d42015-07-23 15:00:04 -0700482 if (self._firmware_rw_build_spec and
Dan Shi59562a82016-01-06 16:05:31 -0800483 self._firmware_rw_build_spec not in ['firmware', 'cros']):
Dan Shia25e0d42015-07-23 15:00:04 -0700484 raise MalformedConfigEntry(
Dan Shi59562a82016-01-06 16:05:31 -0800485 'firmware_rw_build_spec can only be empty, firmware or '
486 'cros. It does not support other build type yet.')
Chris Masone96f16632012-04-04 18:36:03 -0700487
Dan Shi84585362016-10-03 14:11:43 -0700488 if os_type not in OS_TYPES_LAUNCH_CONTROL and self._testbed_dut_count:
489 raise MalformedConfigEntry(
490 'testbed_dut_count is only applicable to testbed to run '
491 'test with builds from Launch Control.')
492
Chris Masone96f16632012-04-04 18:36:03 -0700493 self._bare_branches = []
Dan Shiaceb91d2013-02-20 12:41:28 -0800494 self._version_equal_constraint = False
Dan Shibde10772015-08-18 10:15:58 -0700495 self._version_gte_constraint = False
496 self._version_lte_constraint = False
Chris Masone67f06d62012-04-12 15:16:56 -0700497 if not branch_specs:
498 # Any milestone is OK.
499 self._numeric_constraint = version.LooseVersion('0')
500 else:
501 self._numeric_constraint = None
502 for spec in branch_specs:
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700503 if 'tot' in spec.lower():
504 tot_str = spec[spec.index('tot'):]
505 spec = spec.replace(
506 tot_str, TotMilestoneManager().ConvertTotSpec(
Dan Shibde10772015-08-18 10:15:58 -0700507 tot_str))
Chris Masone67f06d62012-04-12 15:16:56 -0700508 if spec.startswith('>='):
509 self._numeric_constraint = version.LooseVersion(
Dan Shibde10772015-08-18 10:15:58 -0700510 spec.lstrip('>=R'))
511 self._version_gte_constraint = True
512 elif spec.startswith('<='):
513 self._numeric_constraint = version.LooseVersion(
514 spec.lstrip('<=R'))
515 self._version_lte_constraint = True
Dan Shiaceb91d2013-02-20 12:41:28 -0800516 elif spec.startswith('=='):
517 self._version_equal_constraint = True
518 self._numeric_constraint = version.LooseVersion(
Dan Shibde10772015-08-18 10:15:58 -0700519 spec.lstrip('==R'))
Chris Masone67f06d62012-04-12 15:16:56 -0700520 else:
521 self._bare_branches.append(spec)
Alex Millerf2b57442013-09-07 18:40:02 -0700522
Aviv Keshet52c7a212015-12-07 15:27:22 -0800523 # Since we expect __hash__() and other comparator methods to be used
Chris Masonefad911a2012-03-29 12:30:26 -0700524 # frequently by set operations, and they use str() a lot, pre-compute
525 # the string representation of this object.
Aviv Keshet8ce3c982013-01-24 09:23:13 -0800526 if num is None:
527 numStr = '[Default num]'
528 else:
529 numStr = '%d' % num
Alex Millerf2b57442013-09-07 18:40:02 -0700530
531 if boards is None:
532 self._boards = set()
533 boardsStr = '[All boards]'
534 else:
535 self._boards = set([x.strip() for x in boards.split(',')])
536 boardsStr = boards
537
Dan Shi481b8b62016-03-08 13:03:08 -0800538 time_str = ''
539 if self._hour:
540 time_str = ' Run at %d:00.' % self._hour
541 elif self._day:
542 time_str = ' Run on %s.' % _WEEKDAYS[self._day]
Dan Shi2121a332016-02-25 14:22:22 -0800543 if os_type == OS_TYPE_CROS:
544 self._str = ('%s: %s on %s with pool %s, boards [%s], file_bugs = '
Dan Shi481b8b62016-03-08 13:03:08 -0800545 '%s across %s machines.%s' %
Dan Shi2121a332016-02-25 14:22:22 -0800546 (self.__class__.__name__, suite, branch_specs, pool,
Dan Shi481b8b62016-03-08 13:03:08 -0800547 boardsStr, self._file_bugs, numStr, time_str))
Dan Shi2121a332016-02-25 14:22:22 -0800548 else:
Dan Shi84585362016-10-03 14:11:43 -0700549 testbed_dut_count_str = '.'
550 if self._testbed_dut_count:
551 testbed_dut_count_str = (', each with %d duts.' %
552 self._testbed_dut_count)
Dan Shi2121a332016-02-25 14:22:22 -0800553 self._str = ('%s: %s on branches %s and targets %s with pool %s, '
Dan Shi84585362016-10-03 14:11:43 -0700554 'boards [%s], file_bugs = %s across %s machines%s%s' %
Dan Shi2121a332016-02-25 14:22:22 -0800555 (self.__class__.__name__, suite,
556 launch_control_branches, launch_control_targets,
Dan Shi84585362016-10-03 14:11:43 -0700557 pool, boardsStr, self._file_bugs, numStr,
558 testbed_dut_count_str, time_str))
Chris Masone96f16632012-04-04 18:36:03 -0700559
560
561 def _FitsSpec(self, branch):
562 """Checks if a branch is deemed OK by this instance's branch specs.
563
564 When called on a branch name, will return whether that branch
Dan Shiaceb91d2013-02-20 12:41:28 -0800565 'fits' the specifications stored in self._bare_branches,
Dan Shibde10772015-08-18 10:15:58 -0700566 self._numeric_constraint, self._version_equal_constraint,
567 self._version_gte_constraint and self._version_lte_constraint.
Chris Masone96f16632012-04-04 18:36:03 -0700568
569 @param branch: the branch to check.
570 @return True if b 'fits' with stored specs, False otherwise.
571 """
Chris Masone657e1552012-05-30 17:06:20 -0700572 if branch in BARE_BRANCHES:
573 return branch in self._bare_branches
Dan Shiaceb91d2013-02-20 12:41:28 -0800574 if self._numeric_constraint:
575 if self._version_equal_constraint:
576 return version.LooseVersion(branch) == self._numeric_constraint
Dan Shibde10772015-08-18 10:15:58 -0700577 elif self._version_gte_constraint:
578 return version.LooseVersion(branch) >= self._numeric_constraint
579 elif self._version_lte_constraint:
580 return version.LooseVersion(branch) <= self._numeric_constraint
Dan Shiaceb91d2013-02-20 12:41:28 -0800581 else:
Dan Shibde10772015-08-18 10:15:58 -0700582 # Default to great or equal constraint.
Dan Shiaceb91d2013-02-20 12:41:28 -0800583 return version.LooseVersion(branch) >= self._numeric_constraint
584 else:
585 return False
Chris Masonefad911a2012-03-29 12:30:26 -0700586
587
588 @property
Alex Miller9979b5a2012-11-01 17:36:12 -0700589 def name(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800590 """Name of this task, e.g. 'NightlyPower'."""
Alex Miller9979b5a2012-11-01 17:36:12 -0700591 return self._name
592
593
594 @property
Chris Masonefad911a2012-03-29 12:30:26 -0700595 def suite(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800596 """Name of the suite to run, e.g. 'bvt'."""
Chris Masonefad911a2012-03-29 12:30:26 -0700597 return self._suite
598
599
600 @property
Chris Masone96f16632012-04-04 18:36:03 -0700601 def branch_specs(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800602 """a pre-vetted iterable of branch specifiers,
603 e.g. ['>=R18', 'factory']."""
Chris Masone96f16632012-04-04 18:36:03 -0700604 return self._branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700605
606
607 @property
Chris Masone3fba86f2012-04-03 10:06:56 -0700608 def pool(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800609 """The pool of machines to use for scheduling purposes."""
Chris Masone3fba86f2012-04-03 10:06:56 -0700610 return self._pool
Chris Masonefad911a2012-03-29 12:30:26 -0700611
612
Alex Miller9979b5a2012-11-01 17:36:12 -0700613 @property
614 def num(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800615 """The number of devices across which to shard the test suite.
616 Type: integer or None"""
Alex Miller9979b5a2012-11-01 17:36:12 -0700617 return self._num
618
619
Alex Millerf2b57442013-09-07 18:40:02 -0700620 @property
621 def boards(self):
622 """The boards on which to run this suite.
623 Type: Iterable of strings"""
624 return self._boards
625
626
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700627 @property
628 def priority(self):
629 """The priority of the suite"""
630 return self._priority
631
632
633 @property
634 def timeout(self):
635 """The maximum lifetime of the suite in hours."""
636 return self._timeout
637
638
Dan Shia25e0d42015-07-23 15:00:04 -0700639 @property
640 def cros_build_spec(self):
641 """The build spec of ChromeOS to test with a firmware build."""
642 return self._cros_build_spec
643
644
645 @property
646 def firmware_rw_build_spec(self):
Dan Shib49bb8b2016-03-01 15:29:27 -0800647 """The build spec of RW firmware to test with a ChromeOS build.
648
649 The value can be firmware or cros.
650 """
Dan Shia25e0d42015-07-23 15:00:04 -0700651 return self._firmware_rw_build_spec
652
653
654 @property
Dan Shib49bb8b2016-03-01 15:29:27 -0800655 def firmware_ro_build_spec(self):
656 """The build spec of RO firmware to test with a ChromeOS build.
657
658 The value can be stable, firmware or cros, where stable is the stable
659 firmware build retrieved from stable_version table.
660 """
661 return self._firmware_ro_build_spec
662
663
664 @property
Dan Shia25e0d42015-07-23 15:00:04 -0700665 def test_source(self):
Dan Shib49bb8b2016-03-01 15:29:27 -0800666 """Source of the test code, value can be `firmware_rw`, `firmware_ro` or
667 `cros`."""
Dan Shia25e0d42015-07-23 15:00:04 -0700668 return self._test_source
669
670
Dan Shi2121a332016-02-25 14:22:22 -0800671 @property
672 def hour(self):
673 """An integer specifying the hour that a nightly run should be triggered
674 """
675 return self._hour
676
677
678 @property
679 def day(self):
680 """An integer specifying the day of a week that a weekly run should be
681 triggered"""
682 return self._day
683
684
685 @property
686 def os_type(self):
687 """Type of OS, e.g., cros, brillo, android."""
688 return self._os_type
689
690
691 @property
692 def launch_control_branches(self):
693 """A list of Launch Control builds."""
694 return self._launch_control_branches
695
696
697 @property
698 def launch_control_targets(self):
699 """A list of Launch Control targets."""
700 return self._launch_control_targets
701
702
Chris Masonefad911a2012-03-29 12:30:26 -0700703 def __str__(self):
704 return self._str
705
706
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700707 def __repr__(self):
708 return self._str
709
710
Chris Masonefad911a2012-03-29 12:30:26 -0700711 def __lt__(self, other):
712 return str(self) < str(other)
713
714
715 def __le__(self, other):
716 return str(self) <= str(other)
717
718
719 def __eq__(self, other):
720 return str(self) == str(other)
721
722
723 def __ne__(self, other):
724 return str(self) != str(other)
725
726
727 def __gt__(self, other):
728 return str(self) > str(other)
729
730
731 def __ge__(self, other):
732 return str(self) >= str(other)
733
734
735 def __hash__(self):
736 """Allows instances to be correctly deduped when used in a set."""
737 return hash(str(self))
738
739
Dan Shia25e0d42015-07-23 15:00:04 -0700740 def _GetCrOSBuild(self, mv, board):
741 """Get the ChromeOS build name to test with firmware build.
742
743 The ChromeOS build to be used is determined by `self.cros_build_spec`.
744 Its value can be:
745 tot: use the latest ToT build.
746 tot-x: use the latest build in x milestone before ToT.
747 Rxx: use the latest build on xx milestone.
748
749 @param board: the board against which to run self._suite.
750 @param mv: an instance of manifest_versions.ManifestVersions.
751
752 @return: The ChromeOS build name to test with firmware build.
753
754 """
755 if not self.cros_build_spec:
756 return None
757 if self.cros_build_spec.startswith('tot'):
758 milestone = TotMilestoneManager().ConvertTotSpec(
759 self.cros_build_spec)[1:]
760 elif self.cros_build_spec.startswith('R'):
761 milestone = self.cros_build_spec[1:]
762 milestone, latest_manifest = mv.GetLatestManifest(
763 board, 'release', milestone=milestone)
764 latest_build = base_event.BuildName(board, 'release', milestone,
765 latest_manifest)
766 logging.debug('Found latest build of %s for spec %s: %s',
767 board, self.cros_build_spec, latest_build)
768 return latest_build
769
770
Dan Shib49bb8b2016-03-01 15:29:27 -0800771 def _GetFirmwareBuild(self, spec, mv, board):
772 """Get the firmware build name to test with ChromeOS build.
Dan Shia25e0d42015-07-23 15:00:04 -0700773
Dan Shi648ce922016-03-26 00:22:47 -0700774 @param spec: build spec for RO or RW firmware, e.g., firmware, cros.
775 For RO firmware, the value can also be in the format of
776 released_ro_X, where X is the index of the list or RO builds
777 defined in global config RELEASED_RO_BUILDS_[board].
778 For example, for spec `released_ro_2`, and global config
779 CROS/RELEASED_RO_BUILDS_veyron_jerry: build1,build2
780 the return firmare RO build should be build2.
Dan Shia25e0d42015-07-23 15:00:04 -0700781 @param mv: an instance of manifest_versions.ManifestVersions.
782 @param board: the board against which to run self._suite.
Dan Shia25e0d42015-07-23 15:00:04 -0700783
Dan Shib49bb8b2016-03-01 15:29:27 -0800784 @return: The firmware build name to test with ChromeOS build.
Dan Shia25e0d42015-07-23 15:00:04 -0700785 """
Dan Shib49bb8b2016-03-01 15:29:27 -0800786 if spec == 'stable':
787 # TODO(crbug.com/577316): Query stable RO firmware.
788 raise NotImplementedError('`stable` RO firmware build is not '
789 'supported yet.')
790 if not spec:
Dan Shia25e0d42015-07-23 15:00:04 -0700791 return None
Dan Shi648ce922016-03-26 00:22:47 -0700792
793 if spec.startswith('released_ro_'):
794 index = int(spec[12:])
795 released_ro_builds = CONFIG.get_config_value(
796 'CROS', 'RELEASED_RO_BUILDS_%s' % board, type=str,
797 default='').split(',')
798 if not released_ro_builds or len(released_ro_builds) < index:
799 return None
800 else:
801 return released_ro_builds[index-1]
802
Dan Shib49bb8b2016-03-01 15:29:27 -0800803 # build_type is the build type of the firmware build, e.g., factory,
804 # firmware or release. If spec is set to cros, build type should be
805 # mapped to release.
806 build_type = 'release' if spec == 'cros' else spec
Dan Shia25e0d42015-07-23 15:00:04 -0700807 latest_milestone, latest_manifest = mv.GetLatestManifest(
808 board, build_type)
809 latest_build = base_event.BuildName(board, build_type, latest_milestone,
810 latest_manifest)
811 logging.debug('Found latest firmware build of %s for spec %s: %s',
Dan Shib49bb8b2016-03-01 15:29:27 -0800812 board, spec, latest_build)
Dan Shia25e0d42015-07-23 15:00:04 -0700813 return latest_build
814
815
Alex Miller7ce1b362012-07-10 09:24:10 -0700816 def AvailableHosts(self, scheduler, board):
817 """Query what hosts are able to run a test on a board and pool
818 combination.
Alex Miller511a9e32012-07-03 09:16:47 -0700819
820 @param scheduler: an instance of DedupingScheduler, as defined in
821 deduping_scheduler.py
822 @param board: the board against which one wants to run the test.
Alex Miller7ce1b362012-07-10 09:24:10 -0700823 @return The list of hosts meeting the board and pool requirements,
824 or None if no hosts were found."""
Alex Millerf2b57442013-09-07 18:40:02 -0700825 if self._boards and board not in self._boards:
826 return []
827
Dan Shi8a1fbac2016-10-13 12:36:18 -0700828 board_label = Labels.BOARD_PREFIX + board
829 if self._testbed_dut_count:
830 board_label += '-%d' % self._testbed_dut_count
831 labels = [board_label]
Alex Miller511a9e32012-07-03 09:16:47 -0700832 if self._pool:
Chris Masonecd214e02012-07-10 16:22:10 -0700833 labels.append(Labels.POOL_PREFIX + self._pool)
Alex Miller511a9e32012-07-03 09:16:47 -0700834
Prashanth B6de2bde2014-03-25 18:45:02 -0700835 return scheduler.CheckHostsExist(multiple_labels=labels)
Alex Miller511a9e32012-07-03 09:16:47 -0700836
837
Alex Millerd621cf22012-07-11 13:57:10 -0700838 def ShouldHaveAvailableHosts(self):
839 """As a sanity check, return true if we know for certain that
840 we should be able to schedule this test. If we claim this test
841 should be able to run, and it ends up not being scheduled, then
842 a warning will be reported.
843
844 @return True if this test should be able to run, False otherwise.
845 """
846 return self._pool == 'bvt'
847
848
Dan Shi2121a332016-02-25 14:22:22 -0800849 def _ScheduleSuite(self, scheduler, cros_build, firmware_rw_build,
Dan Shib49bb8b2016-03-01 15:29:27 -0800850 firmware_ro_build, test_source_build,
851 launch_control_build, board, force, run_prod_code=False):
Dan Shi2121a332016-02-25 14:22:22 -0800852 """Try to schedule a suite with given build and board information.
853
854 @param scheduler: an instance of DedupingScheduler, as defined in
855 deduping_scheduler.py
856 @oaran build: Build to run suite for, e.g., 'daisy-release/R18-1655.0.0'
857 and 'git_mnc_release/shamu-eng/123'.
858 @param firmware_rw_build: Firmware RW build to run test with.
Dan Shib49bb8b2016-03-01 15:29:27 -0800859 @param firmware_ro_build: Firmware RO build to run test with.
Dan Shi2121a332016-02-25 14:22:22 -0800860 @param test_source_build: Test source build, used for server-side
861 packaging.
862 @param launch_control_build: Name of a Launch Control build, e.g.,
863 'git_mnc_release/shamu-eng/123'
864 @param board: the board against which to run self._suite.
865 @param force: Always schedule the suite.
866 @param run_prod_code: If True, the suite will run the test code that
867 lives in prod aka the test code currently on the
868 lab servers. If False, the control files and test
869 code for this suite run will be retrieved from the
870 build artifacts. Default is False.
871 """
872 test_source_build_msg = (
873 ' Test source build is %s.' % test_source_build
Dan Shi8446fac2016-03-02 22:07:39 -0800874 if test_source_build else '')
Dan Shi2121a332016-02-25 14:22:22 -0800875 firmware_rw_build_msg = (
876 ' Firmware RW build is %s.' % firmware_rw_build
Dan Shi8446fac2016-03-02 22:07:39 -0800877 if firmware_rw_build else '')
Dan Shib49bb8b2016-03-01 15:29:27 -0800878 firmware_ro_build_msg = (
879 ' Firmware RO build is %s.' % firmware_ro_build
Dan Shi8446fac2016-03-02 22:07:39 -0800880 if firmware_ro_build else '')
Dan Shi84585362016-10-03 14:11:43 -0700881 # If testbed_dut_count is set, the suite is for testbed. Update build
882 # and board with the dut count.
883 if self._testbed_dut_count:
884 launch_control_build = '%s#%d' % (launch_control_build,
885 self._testbed_dut_count)
886 test_source_build = launch_control_build
887 board = '%s-%d' % (board, self._testbed_dut_count)
Dan Shi2121a332016-02-25 14:22:22 -0800888 build_string = cros_build or launch_control_build
Dan Shib49bb8b2016-03-01 15:29:27 -0800889 logging.debug('Schedule %s for build %s.%s%s%s',
Dan Shi2121a332016-02-25 14:22:22 -0800890 self._suite, build_string, test_source_build_msg,
Dan Shib49bb8b2016-03-01 15:29:27 -0800891 firmware_rw_build_msg, firmware_ro_build_msg)
Dan Shi2121a332016-02-25 14:22:22 -0800892
893 if not scheduler.ScheduleSuite(
894 self._suite, board, cros_build, self._pool, self._num,
895 self._priority, self._timeout, force,
896 file_bugs=self._file_bugs,
897 firmware_rw_build=firmware_rw_build,
Dan Shib49bb8b2016-03-01 15:29:27 -0800898 firmware_ro_build=firmware_ro_build,
Dan Shi2121a332016-02-25 14:22:22 -0800899 test_source_build=test_source_build,
900 job_retry=self._job_retry,
901 launch_control_build=launch_control_build,
Dan Shi84585362016-10-03 14:11:43 -0700902 run_prod_code=run_prod_code,
Dan Shi1d0e6392016-10-26 13:02:37 -0700903 testbed_dut_count=self._testbed_dut_count,
904 no_delay=self._no_delay):
Dan Shi2121a332016-02-25 14:22:22 -0800905 logging.info('Skipping scheduling %s on %s for %s',
906 self._suite, build_string, board)
907
908
909 def _Run_CrOS_Builds(self, scheduler, branch_builds, board, force=False,
910 mv=None):
911 """Run this task for CrOS builds. Returns False if it should be
912 destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -0700913
Chris Masone013859b2012-04-01 13:45:26 -0700914 Execute this task. Attempt to schedule the associated suite.
915 Return True if this task should be kept around, False if it
916 should be destroyed. This allows for one-shot Tasks.
Chris Masonefad911a2012-03-29 12:30:26 -0700917
918 @param scheduler: an instance of DedupingScheduler, as defined in
919 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -0700920 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -0700921 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -0700922 {'R18': ['x86-alex-release/R18-1655.0.0'],
923 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -0700924 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -0700925 @param force: Always schedule the suite.
Dan Shia25e0d42015-07-23 15:00:04 -0700926 @param mv: an instance of manifest_versions.ManifestVersions.
927
Chris Masone013859b2012-04-01 13:45:26 -0700928 @return True if the task should be kept, False if not
Dan Shia25e0d42015-07-23 15:00:04 -0700929
Chris Masonefad911a2012-03-29 12:30:26 -0700930 """
Chris Masonea3a38172012-05-14 15:19:56 -0700931 logging.info('Running %s on %s', self._name, board)
Dan Shia25e0d42015-07-23 15:00:04 -0700932 is_firmware_build = 'firmware' in self.branch_specs
Dan Shib49bb8b2016-03-01 15:29:27 -0800933
934 # firmware_xx_build is only needed if firmware_xx_build_spec is given.
Dan Shia25e0d42015-07-23 15:00:04 -0700935 firmware_rw_build = None
Dan Shib49bb8b2016-03-01 15:29:27 -0800936 firmware_ro_build = None
Dan Shia25e0d42015-07-23 15:00:04 -0700937 try:
938 if is_firmware_build:
939 # When build specified in branch_specs is a firmware build,
940 # we need a ChromeOS build to test with the firmware build.
941 cros_build = self._GetCrOSBuild(mv, board)
Dan Shib49bb8b2016-03-01 15:29:27 -0800942 elif self.firmware_rw_build_spec or self.firmware_ro_build_spec:
943 # When firmware_xx_build_spec is specified, the test involves
944 # updating the RW firmware by firmware build specified in
945 # firmware_xx_build_spec.
946 firmware_rw_build = self._GetFirmwareBuild(
947 self.firmware_rw_build_spec, mv, board)
948 firmware_ro_build = self._GetFirmwareBuild(
949 self.firmware_ro_build_spec, mv, board)
Dan Shi648ce922016-03-26 00:22:47 -0700950 # If RO firmware is specified, force to create suite, because
951 # dedupe based on test source build does not reflect the change
952 # of RO firmware.
953 if firmware_ro_build:
954 force = True
Dan Shia25e0d42015-07-23 15:00:04 -0700955 except manifest_versions.QueryException as e:
956 logging.error(e)
957 logging.error('Running %s on %s is failed. Failed to find build '
958 'required to run the suite.', self._name, board)
959 return False
960
Dan Shi648ce922016-03-26 00:22:47 -0700961 # Return if there is no firmware RO build found for given spec.
962 if not firmware_ro_build and self.firmware_ro_build_spec:
963 return True
964
Chris Masone96f16632012-04-04 18:36:03 -0700965 builds = []
Chris Masonefe5a5092012-04-11 18:29:07 -0700966 for branch, build in branch_builds.iteritems():
Chris Masonea3a38172012-05-14 15:19:56 -0700967 logging.info('Checking if %s fits spec %r',
968 branch, self.branch_specs)
Chris Masone96f16632012-04-04 18:36:03 -0700969 if self._FitsSpec(branch):
Dan Shibde10772015-08-18 10:15:58 -0700970 logging.debug('Build %s fits the spec.', build)
Chris Masone05b19442012-04-17 13:37:55 -0700971 builds.extend(build)
Chris Masone96f16632012-04-04 18:36:03 -0700972 for build in builds:
Chris Masone3fba86f2012-04-03 10:06:56 -0700973 try:
Dan Shia25e0d42015-07-23 15:00:04 -0700974 if is_firmware_build:
975 firmware_rw_build = build
976 else:
977 cros_build = build
978 if self.test_source == Builds.FIRMWARE_RW:
979 test_source_build = firmware_rw_build
980 elif self.test_source == Builds.CROS:
981 test_source_build = cros_build
982 else:
983 test_source_build = None
Dan Shi2121a332016-02-25 14:22:22 -0800984 self._ScheduleSuite(scheduler, cros_build, firmware_rw_build,
Dan Shib49bb8b2016-03-01 15:29:27 -0800985 firmware_ro_build, test_source_build,
986 None, board, force)
Chris Masone3fba86f2012-04-03 10:06:56 -0700987 except deduping_scheduler.DedupingSchedulerException as e:
988 logging.error(e)
Chris Masonefad911a2012-03-29 12:30:26 -0700989 return True
990
991
Dan Shi2121a332016-02-25 14:22:22 -0800992 def _Run_LaunchControl_Builds(self, scheduler, launch_control_builds, board,
993 force=False):
994 """Run this task. Returns False if it should be destroyed.
995
996 Execute this task. Attempt to schedule the associated suite.
997 Return True if this task should be kept around, False if it
998 should be destroyed. This allows for one-shot Tasks.
999
1000 @param scheduler: an instance of DedupingScheduler, as defined in
1001 deduping_scheduler.py
1002 @param launch_control_builds: A list of Launch Control builds.
1003 @param board: the board against which to run self._suite.
1004 @param force: Always schedule the suite.
1005
1006 @return True if the task should be kept, False if not
1007
1008 """
1009 logging.info('Running %s on %s', self._name, board)
1010 for build in launch_control_builds:
1011 try:
Dan Shi8446fac2016-03-02 22:07:39 -08001012 self._ScheduleSuite(scheduler, None, None, None,
1013 test_source_build=build,
Dan Shi2121a332016-02-25 14:22:22 -08001014 launch_control_build=build, board=board,
1015 force=force, run_prod_code=True)
1016 except deduping_scheduler.DedupingSchedulerException as e:
1017 logging.error(e)
1018 return True
1019
1020
1021 def Run(self, scheduler, branch_builds, board, force=False, mv=None,
1022 launch_control_builds=None):
1023 """Run this task. Returns False if it should be destroyed.
1024
1025 Execute this task. Attempt to schedule the associated suite.
1026 Return True if this task should be kept around, False if it
1027 should be destroyed. This allows for one-shot Tasks.
1028
1029 @param scheduler: an instance of DedupingScheduler, as defined in
1030 deduping_scheduler.py
1031 @param branch_builds: a dict mapping branch name to the build(s) to
1032 install for that branch, e.g.
1033 {'R18': ['x86-alex-release/R18-1655.0.0'],
1034 'R19': ['x86-alex-release/R19-2077.0.0']}
1035 @param board: the board against which to run self._suite.
1036 @param force: Always schedule the suite.
1037 @param mv: an instance of manifest_versions.ManifestVersions.
1038 @param launch_control_builds: A list of Launch Control builds.
1039
1040 @return True if the task should be kept, False if not
1041
1042 """
1043 if ((self._os_type == OS_TYPE_CROS and not branch_builds) or
1044 (self._os_type != OS_TYPE_CROS and not launch_control_builds)):
1045 logging.debug('No build to run, skip running %s on %s.', self._name,
1046 board)
1047 # Return True so the task will be kept, as the given build and board
1048 # do not match.
1049 return True
1050
1051 if self._os_type == OS_TYPE_CROS:
1052 return self._Run_CrOS_Builds(
1053 scheduler, branch_builds, board, force, mv)
1054 else:
1055 return self._Run_LaunchControl_Builds(
1056 scheduler, launch_control_builds, board, force)
1057
1058
Chris Masone013859b2012-04-01 13:45:26 -07001059class OneShotTask(Task):
1060 """A Task that can be run only once. Can schedule itself."""
Chris Masonefad911a2012-03-29 12:30:26 -07001061
1062
Dan Shi2121a332016-02-25 14:22:22 -08001063 def Run(self, scheduler, branch_builds, board, force=False, mv=None,
1064 launch_control_builds=None):
Chris Masone013859b2012-04-01 13:45:26 -07001065 """Run this task. Returns False, indicating it should be destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -07001066
Chris Masone013859b2012-04-01 13:45:26 -07001067 Run this task. Attempt to schedule the associated suite.
1068 Return False, indicating to the caller that it should discard this task.
Chris Masonefad911a2012-03-29 12:30:26 -07001069
1070 @param scheduler: an instance of DedupingScheduler, as defined in
1071 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -07001072 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -07001073 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -07001074 {'R18': ['x86-alex-release/R18-1655.0.0'],
1075 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -07001076 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -07001077 @param force: Always schedule the suite.
Dan Shia25e0d42015-07-23 15:00:04 -07001078 @param mv: an instance of manifest_versions.ManifestVersions.
Dan Shi2121a332016-02-25 14:22:22 -08001079 @param launch_control_builds: A list of Launch Control builds.
Dan Shia25e0d42015-07-23 15:00:04 -07001080
Chris Masonefad911a2012-03-29 12:30:26 -07001081 @return False
Dan Shia25e0d42015-07-23 15:00:04 -07001082
Chris Masonefad911a2012-03-29 12:30:26 -07001083 """
Dan Shia25e0d42015-07-23 15:00:04 -07001084 super(OneShotTask, self).Run(scheduler, branch_builds, board, force,
Dan Shi2121a332016-02-25 14:22:22 -08001085 mv, launch_control_builds)
Chris Masonefad911a2012-03-29 12:30:26 -07001086 return False