blob: fc19bab4e1f07f3d8dd6b014c3c17856a8c860cf [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
Chris Masone96f16632012-04-04 18:36:03 -07006import logging, re
Aviv Keshetd83ef442013-01-16 16:19:35 -08007import deduping_scheduler
Alex Millerc7bcf8b2013-09-07 20:13:20 -07008import driver
Chris Masone67f06d62012-04-12 15:16:56 -07009from distutils import version
Alex Miller511a9e32012-07-03 09:16:47 -070010from constants import Labels
Chris Masone96f16632012-04-04 18:36:03 -070011
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070012import common
13from autotest_lib.server.cros.dynamic_suite import constants
14from autotest_lib.scheduler import scheduler_lib
15from autotest_lib.server import site_utils
16try:
17 from chromite.lib import gs
18except ImportError:
19 gs = None
20
Chris Masone96f16632012-04-04 18:36:03 -070021
22class MalformedConfigEntry(Exception):
23 """Raised to indicate a failure to parse a Task out of a config."""
24 pass
Chris Masonefad911a2012-03-29 12:30:26 -070025
26
Alex Miller0d003572013-03-18 11:51:30 -070027BARE_BRANCHES = ['factory', 'firmware']
Chris Masone67f06d62012-04-12 15:16:56 -070028
29
30def PickBranchName(type, milestone):
Dan Shiaceb91d2013-02-20 12:41:28 -080031 """Pick branch name. If type is among BARE_BRANCHES, return type,
32 otherwise, return milestone.
33
34 @param type: type of the branch, e.g., 'release', 'factory', or 'firmware'
35 @param milestone: CrOS milestone number
36 """
Chris Masone67f06d62012-04-12 15:16:56 -070037 if type in BARE_BRANCHES:
38 return type
39 return milestone
40
41
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070042class TotMilestoneManager(object):
43 """A class capable of converting tot string to milestone numbers.
44
45 This class is used as a cache for the tot milestone, so we don't
46 repeatedly hit google storage for all O(100) tasks in suite
47 scheduler's ini file.
48 """
49
50 __metaclass__ = scheduler_lib.Singleton
51
52 @staticmethod
53 def _tot_milestone():
54 """Get the tot milestone, eg: R40
55
56 @returns: A string representing the Tot milestone as declared by
57 the LATEST_BUILD_URL, or an empty string if LATEST_BUILD_URL
58 doesn't exist.
59 """
60 try:
61 return (gs.GSContext().Cat(constants.LATEST_BUILD_URL).split('-')[0]
62 if gs else '')
63 except gs.GSNoSuchKey as e:
64 logging.warning('Failed to get latest build: %s', e)
65 return ''
66
67
68 def refresh(self):
69 """Refresh the tot milestone string managed by this class."""
70 self.tot = self._tot_milestone()
71
72
73 def __init__(self):
74 """Initialize a TotMilestoneManager."""
75 self.refresh()
76
77
78 def ConvertTotSpec(self, tot_spec):
79 """Converts a tot spec to the appropriate milestone.
80
81 Assume tot is R40:
82 tot -> R40
83 tot-1 -> R39
84 tot-2 -> R38
85 tot-(any other numbers) -> R40
86
87 With the last option one assumes that a malformed configuration that has
88 'tot' in it, wants at least tot.
89
90 @param tot_spec: A string representing the tot spec.
91 @raises MalformedConfigEntry: If the tot_spec doesn't match the
92 expected format.
93 """
94 tot_spec = tot_spec.lower()
95 match = re.match('(tot)[-]?(1$|2$)?', tot_spec)
96 if not match:
97 raise MalformedConfigEntry(
98 "%s isn't a valid branch spec." % tot_spec)
99 tot_mstone = self.tot
100 num_back = match.groups()[1]
101 if num_back:
102 tot_mstone_num = tot_mstone.lstrip('R')
103 tot_mstone = tot_mstone.replace(
104 tot_mstone_num, str(int(tot_mstone_num)-int(num_back)))
105 return tot_mstone
106
107
Chris Masone013859b2012-04-01 13:45:26 -0700108class Task(object):
Chris Masonefad911a2012-03-29 12:30:26 -0700109 """Represents an entry from the scheduler config. Can schedule itself.
110
111 Each entry from the scheduler config file maps one-to-one to a
Chris Masone013859b2012-04-01 13:45:26 -0700112 Task. Each instance has enough info to schedule itself
Chris Masonefad911a2012-03-29 12:30:26 -0700113 on-demand with the AFE.
114
115 This class also overrides __hash__() and all comparitor methods to enable
116 correct use in dicts, sets, etc.
117 """
118
Chris Masone96f16632012-04-04 18:36:03 -0700119
120 @staticmethod
121 def CreateFromConfigSection(config, section):
122 """Create a Task from a section of a config file.
123
124 The section to parse should look like this:
125 [TaskName]
126 suite: suite_to_run # Required
127 run_on: event_on which to run # Required
Dan Shiaceb91d2013-02-20 12:41:28 -0800128 branch_specs: factory,firmware,>=R12 or ==R12 # Optional
Chris Masone96f16632012-04-04 18:36:03 -0700129 pool: pool_of_devices # Optional
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700130 num: sharding_factor # int, Optional
Alex Millerf2b57442013-09-07 18:40:02 -0700131 boards: board1, board2 # comma seperated string, Optional
Chris Masone96f16632012-04-04 18:36:03 -0700132
Chris Masone67f06d62012-04-12 15:16:56 -0700133 By default, Tasks run on all release branches, not factory or firmware.
134
Chris Masone96f16632012-04-04 18:36:03 -0700135 @param config: a ForgivingConfigParser.
136 @param section: the section to parse into a Task.
137 @return keyword, Task object pair. One or both will be None on error.
138 @raise MalformedConfigEntry if there's a problem parsing |section|.
139 """
Alex Miller06695022012-07-18 09:31:36 -0700140 if not config.has_section(section):
141 raise MalformedConfigEntry('unknown section %s' % section)
142
Alex Millerf2b57442013-09-07 18:40:02 -0700143 allowed = set(['suite', 'run_on', 'branch_specs', 'pool', 'num',
Prashanth B6de2bde2014-03-25 18:45:02 -0700144 'boards', 'file_bugs'])
Alex Millerbb535e22012-07-11 20:11:33 -0700145 # The parameter of union() is the keys under the section in the config
146 # The union merges this with the allowed set, so if any optional keys
147 # are omitted, then they're filled in. If any extra keys are present,
148 # then they will expand unioned set, causing it to fail the following
149 # comparison against the allowed set.
150 section_headers = allowed.union(dict(config.items(section)).keys())
151 if allowed != section_headers:
152 raise MalformedConfigEntry('unknown entries: %s' %
153 ", ".join(map(str, section_headers.difference(allowed))))
154
Chris Masone96f16632012-04-04 18:36:03 -0700155 keyword = config.getstring(section, 'run_on')
156 suite = config.getstring(section, 'suite')
157 branches = config.getstring(section, 'branch_specs')
158 pool = config.getstring(section, 'pool')
Alex Millerf2b57442013-09-07 18:40:02 -0700159 boards = config.getstring(section, 'boards')
Prashanth B6de2bde2014-03-25 18:45:02 -0700160 file_bugs = config.getboolean(section, 'file_bugs')
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700161 for klass in driver.Driver.EVENT_CLASSES:
162 if klass.KEYWORD == keyword:
163 priority = klass.PRIORITY
164 timeout = klass.TIMEOUT
165 break
166 else:
167 priority = None
168 timeout = None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700169 try:
170 num = config.getint(section, 'num')
171 except ValueError as e:
172 raise MalformedConfigEntry("Ill-specified 'num': %r" %e)
Chris Masone96f16632012-04-04 18:36:03 -0700173 if not keyword:
174 raise MalformedConfigEntry('No event to |run_on|.')
175 if not suite:
176 raise MalformedConfigEntry('No |suite|')
177 specs = []
178 if branches:
179 specs = re.split('\s*,\s*', branches)
180 Task.CheckBranchSpecs(specs)
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700181 return keyword, Task(section, suite, specs, pool, num, boards,
Prashanth B6de2bde2014-03-25 18:45:02 -0700182 priority, timeout,
183 file_bugs=file_bugs if file_bugs else False)
Chris Masone96f16632012-04-04 18:36:03 -0700184
185
186 @staticmethod
187 def CheckBranchSpecs(branch_specs):
188 """Make sure entries in the list branch_specs are correctly formed.
189
Chris Masone67f06d62012-04-12 15:16:56 -0700190 We accept any of BARE_BRANCHES in |branch_specs|, as
Dan Shiaceb91d2013-02-20 12:41:28 -0800191 well as _one_ string of the form '>=RXX' or '==RXX', where 'RXX' is a
Chris Masone96f16632012-04-04 18:36:03 -0700192 CrOS milestone number.
193
194 @param branch_specs: an iterable of branch specifiers.
195 @raise MalformedConfigEntry if there's a problem parsing |branch_specs|.
196 """
197 have_seen_numeric_constraint = False
198 for branch in branch_specs:
Chris Masone67f06d62012-04-12 15:16:56 -0700199 if branch in BARE_BRANCHES:
Chris Masone96f16632012-04-04 18:36:03 -0700200 continue
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700201 if not have_seen_numeric_constraint:
202 #TODO(beeps): Why was <= dropped on the floor?
203 if branch.startswith('>=R') or branch.startswith('==R'):
204 have_seen_numeric_constraint = True
205 elif 'tot' in branch:
206 TotMilestoneManager().ConvertTotSpec(
207 branch[branch.index('tot'):])
208 have_seen_numeric_constraint = True
Chris Masone96f16632012-04-04 18:36:03 -0700209 continue
Chris Masone97cf0a72012-05-16 09:55:52 -0700210 raise MalformedConfigEntry("%s isn't a valid branch spec." % branch)
Chris Masone96f16632012-04-04 18:36:03 -0700211
212
Alex Millerf2b57442013-09-07 18:40:02 -0700213 def __init__(self, name, suite, branch_specs, pool=None, num=None,
Prashanth B6de2bde2014-03-25 18:45:02 -0700214 boards=None, priority=None, timeout=None, file_bugs=False):
Chris Masonefad911a2012-03-29 12:30:26 -0700215 """Constructor
216
Chris Masone96f16632012-04-04 18:36:03 -0700217 Given an iterable in |branch_specs|, pre-vetted using CheckBranchSpecs,
218 we'll store them such that _FitsSpec() can be used to check whether a
219 given branch 'fits' with the specifications passed in here.
220 For example, given branch_specs = ['factory', '>=R18'], we'd set things
221 up so that _FitsSpec() would return True for 'factory', or 'RXX'
Dan Shiaceb91d2013-02-20 12:41:28 -0800222 where XX is a number >= 18. Same check is done for branch_specs = [
223 'factory', '==R18'], which limit the test to only one specific branch.
Chris Masone96f16632012-04-04 18:36:03 -0700224
225 Given branch_specs = ['factory', 'firmware'], _FitsSpec()
226 would pass only those two specific strings.
227
228 Example usage:
Chris Masonecc4631d2012-04-20 12:06:39 -0700229 t = Task('Name', 'suite', ['factory', '>=R18'])
Chris Masone96f16632012-04-04 18:36:03 -0700230 t._FitsSpec('factory') # True
231 t._FitsSpec('R19') # True
232 t._FitsSpec('R17') # False
233 t._FitsSpec('firmware') # False
234 t._FitsSpec('goober') # False
235
Dan Shiaceb91d2013-02-20 12:41:28 -0800236 t = Task('Name', 'suite', ['factory', '==R18'])
237 t._FitsSpec('R19') # False, branch does not equal to 18
238 t._FitsSpec('R18') # True
239 t._FitsSpec('R17') # False
240
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700241 @param name: name of this task, e.g. 'NightlyPower'
Chris Masonefad911a2012-03-29 12:30:26 -0700242 @param suite: the name of the suite to run, e.g. 'bvt'
Chris Masone96f16632012-04-04 18:36:03 -0700243 @param branch_specs: a pre-vetted iterable of branch specifiers,
244 e.g. ['>=R18', 'factory']
Chris Masonefad911a2012-03-29 12:30:26 -0700245 @param pool: the pool of machines to use for scheduling purposes.
246 Default: None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700247 @param num: the number of devices across which to shard the test suite.
Aviv Keshetd83ef442013-01-16 16:19:35 -0800248 Type: integer or None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700249 Default: None
Alex Millerf2b57442013-09-07 18:40:02 -0700250 @param boards: A comma seperated list of boards to run this task on.
251 Default: Run on all boards.
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700252 @param priority: The string name of a priority from
253 client.common_lib.priorities.Priority.
254 @param timeout: The max lifetime of the suite in hours.
Prashanth B6de2bde2014-03-25 18:45:02 -0700255 @param file_bugs: True if bug filing is desired for the suite created
256 for this task.
Chris Masonefad911a2012-03-29 12:30:26 -0700257 """
Chris Masonecc4631d2012-04-20 12:06:39 -0700258 self._name = name
Chris Masonefad911a2012-03-29 12:30:26 -0700259 self._suite = suite
Chris Masone96f16632012-04-04 18:36:03 -0700260 self._branch_specs = branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700261 self._pool = pool
Aviv Keshetd83ef442013-01-16 16:19:35 -0800262 self._num = num
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700263 self._priority = priority
264 self._timeout = timeout
Prashanth B6de2bde2014-03-25 18:45:02 -0700265 self._file_bugs = file_bugs
Chris Masone96f16632012-04-04 18:36:03 -0700266
267 self._bare_branches = []
Dan Shiaceb91d2013-02-20 12:41:28 -0800268 self._version_equal_constraint = False
Chris Masone67f06d62012-04-12 15:16:56 -0700269 if not branch_specs:
270 # Any milestone is OK.
271 self._numeric_constraint = version.LooseVersion('0')
272 else:
273 self._numeric_constraint = None
274 for spec in branch_specs:
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700275 if 'tot' in spec.lower():
276 tot_str = spec[spec.index('tot'):]
277 spec = spec.replace(
278 tot_str, TotMilestoneManager().ConvertTotSpec(
279 tot_str))
Chris Masone67f06d62012-04-12 15:16:56 -0700280 if spec.startswith('>='):
281 self._numeric_constraint = version.LooseVersion(
282 spec.lstrip('>=R'))
Dan Shiaceb91d2013-02-20 12:41:28 -0800283 elif spec.startswith('=='):
284 self._version_equal_constraint = True
285 self._numeric_constraint = version.LooseVersion(
286 spec.lstrip('==R'))
Chris Masone67f06d62012-04-12 15:16:56 -0700287 else:
288 self._bare_branches.append(spec)
Alex Millerf2b57442013-09-07 18:40:02 -0700289
Chris Masonefad911a2012-03-29 12:30:26 -0700290 # Since we expect __hash__() and other comparitor methods to be used
291 # frequently by set operations, and they use str() a lot, pre-compute
292 # the string representation of this object.
Aviv Keshet8ce3c982013-01-24 09:23:13 -0800293 if num is None:
294 numStr = '[Default num]'
295 else:
296 numStr = '%d' % num
Alex Millerf2b57442013-09-07 18:40:02 -0700297
298 if boards is None:
299 self._boards = set()
300 boardsStr = '[All boards]'
301 else:
302 self._boards = set([x.strip() for x in boards.split(',')])
303 boardsStr = boards
304
Prashanth B6de2bde2014-03-25 18:45:02 -0700305 self._str = ('%s: %s on %s with pool %s, boards [%s], file_bugs = %s '
306 'across %s machines.' % (self.__class__.__name__,
307 suite, branch_specs, pool, boardsStr, self._file_bugs,
308 numStr))
Chris Masone96f16632012-04-04 18:36:03 -0700309
310
311 def _FitsSpec(self, branch):
312 """Checks if a branch is deemed OK by this instance's branch specs.
313
314 When called on a branch name, will return whether that branch
Dan Shiaceb91d2013-02-20 12:41:28 -0800315 'fits' the specifications stored in self._bare_branches,
316 self._numeric_constraint and self._version_equal_constraint.
Chris Masone96f16632012-04-04 18:36:03 -0700317
318 @param branch: the branch to check.
319 @return True if b 'fits' with stored specs, False otherwise.
320 """
Chris Masone657e1552012-05-30 17:06:20 -0700321 if branch in BARE_BRANCHES:
322 return branch in self._bare_branches
Dan Shiaceb91d2013-02-20 12:41:28 -0800323 if self._numeric_constraint:
324 if self._version_equal_constraint:
325 return version.LooseVersion(branch) == self._numeric_constraint
326 else:
327 return version.LooseVersion(branch) >= self._numeric_constraint
328 else:
329 return False
Chris Masonefad911a2012-03-29 12:30:26 -0700330
331
332 @property
Alex Miller9979b5a2012-11-01 17:36:12 -0700333 def name(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800334 """Name of this task, e.g. 'NightlyPower'."""
Alex Miller9979b5a2012-11-01 17:36:12 -0700335 return self._name
336
337
338 @property
Chris Masonefad911a2012-03-29 12:30:26 -0700339 def suite(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800340 """Name of the suite to run, e.g. 'bvt'."""
Chris Masonefad911a2012-03-29 12:30:26 -0700341 return self._suite
342
343
344 @property
Chris Masone96f16632012-04-04 18:36:03 -0700345 def branch_specs(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800346 """a pre-vetted iterable of branch specifiers,
347 e.g. ['>=R18', 'factory']."""
Chris Masone96f16632012-04-04 18:36:03 -0700348 return self._branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700349
350
351 @property
Chris Masone3fba86f2012-04-03 10:06:56 -0700352 def pool(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800353 """The pool of machines to use for scheduling purposes."""
Chris Masone3fba86f2012-04-03 10:06:56 -0700354 return self._pool
Chris Masonefad911a2012-03-29 12:30:26 -0700355
356
Alex Miller9979b5a2012-11-01 17:36:12 -0700357 @property
358 def num(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800359 """The number of devices across which to shard the test suite.
360 Type: integer or None"""
Alex Miller9979b5a2012-11-01 17:36:12 -0700361 return self._num
362
363
Alex Millerf2b57442013-09-07 18:40:02 -0700364 @property
365 def boards(self):
366 """The boards on which to run this suite.
367 Type: Iterable of strings"""
368 return self._boards
369
370
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700371 @property
372 def priority(self):
373 """The priority of the suite"""
374 return self._priority
375
376
377 @property
378 def timeout(self):
379 """The maximum lifetime of the suite in hours."""
380 return self._timeout
381
382
Chris Masonefad911a2012-03-29 12:30:26 -0700383 def __str__(self):
384 return self._str
385
386
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700387 def __repr__(self):
388 return self._str
389
390
Chris Masonefad911a2012-03-29 12:30:26 -0700391 def __lt__(self, other):
392 return str(self) < str(other)
393
394
395 def __le__(self, other):
396 return str(self) <= str(other)
397
398
399 def __eq__(self, other):
400 return str(self) == str(other)
401
402
403 def __ne__(self, other):
404 return str(self) != str(other)
405
406
407 def __gt__(self, other):
408 return str(self) > str(other)
409
410
411 def __ge__(self, other):
412 return str(self) >= str(other)
413
414
415 def __hash__(self):
416 """Allows instances to be correctly deduped when used in a set."""
417 return hash(str(self))
418
419
Alex Miller7ce1b362012-07-10 09:24:10 -0700420 def AvailableHosts(self, scheduler, board):
421 """Query what hosts are able to run a test on a board and pool
422 combination.
Alex Miller511a9e32012-07-03 09:16:47 -0700423
424 @param scheduler: an instance of DedupingScheduler, as defined in
425 deduping_scheduler.py
426 @param board: the board against which one wants to run the test.
Alex Miller7ce1b362012-07-10 09:24:10 -0700427 @return The list of hosts meeting the board and pool requirements,
428 or None if no hosts were found."""
Alex Millerf2b57442013-09-07 18:40:02 -0700429 if self._boards and board not in self._boards:
430 return []
431
Alex Miller511a9e32012-07-03 09:16:47 -0700432 labels = [Labels.BOARD_PREFIX + board]
433 if self._pool:
Chris Masonecd214e02012-07-10 16:22:10 -0700434 labels.append(Labels.POOL_PREFIX + self._pool)
Alex Miller511a9e32012-07-03 09:16:47 -0700435
Prashanth B6de2bde2014-03-25 18:45:02 -0700436 return scheduler.CheckHostsExist(multiple_labels=labels)
Alex Miller511a9e32012-07-03 09:16:47 -0700437
438
Alex Millerd621cf22012-07-11 13:57:10 -0700439 def ShouldHaveAvailableHosts(self):
440 """As a sanity check, return true if we know for certain that
441 we should be able to schedule this test. If we claim this test
442 should be able to run, and it ends up not being scheduled, then
443 a warning will be reported.
444
445 @return True if this test should be able to run, False otherwise.
446 """
447 return self._pool == 'bvt'
448
449
Chris Masone96f16632012-04-04 18:36:03 -0700450 def Run(self, scheduler, branch_builds, board, force=False):
Chris Masone013859b2012-04-01 13:45:26 -0700451 """Run this task. Returns False if it should be destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -0700452
Chris Masone013859b2012-04-01 13:45:26 -0700453 Execute this task. Attempt to schedule the associated suite.
454 Return True if this task should be kept around, False if it
455 should be destroyed. This allows for one-shot Tasks.
Chris Masonefad911a2012-03-29 12:30:26 -0700456
457 @param scheduler: an instance of DedupingScheduler, as defined in
458 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -0700459 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -0700460 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -0700461 {'R18': ['x86-alex-release/R18-1655.0.0'],
462 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -0700463 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -0700464 @param force: Always schedule the suite.
Chris Masone013859b2012-04-01 13:45:26 -0700465 @return True if the task should be kept, False if not
Chris Masonefad911a2012-03-29 12:30:26 -0700466 """
Chris Masonea3a38172012-05-14 15:19:56 -0700467 logging.info('Running %s on %s', self._name, board)
Chris Masone96f16632012-04-04 18:36:03 -0700468 builds = []
Chris Masonefe5a5092012-04-11 18:29:07 -0700469 for branch, build in branch_builds.iteritems():
Chris Masonea3a38172012-05-14 15:19:56 -0700470 logging.info('Checking if %s fits spec %r',
471 branch, self.branch_specs)
Chris Masone96f16632012-04-04 18:36:03 -0700472 if self._FitsSpec(branch):
Chris Masone05b19442012-04-17 13:37:55 -0700473 builds.extend(build)
Chris Masone96f16632012-04-04 18:36:03 -0700474 for build in builds:
Chris Masone3fba86f2012-04-03 10:06:56 -0700475 try:
Chris Masone96f16632012-04-04 18:36:03 -0700476 if not scheduler.ScheduleSuite(self._suite, board, build,
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700477 self._pool, self._num,
478 self._priority, self._timeout,
Prashanth B6de2bde2014-03-25 18:45:02 -0700479 force,
480 file_bugs=self._file_bugs):
Chris Masone96f16632012-04-04 18:36:03 -0700481 logging.info('Skipping scheduling %s on %s for %s',
482 self._suite, build, board)
Chris Masone3fba86f2012-04-03 10:06:56 -0700483 except deduping_scheduler.DedupingSchedulerException as e:
484 logging.error(e)
Chris Masonefad911a2012-03-29 12:30:26 -0700485 return True
486
487
Chris Masone013859b2012-04-01 13:45:26 -0700488class OneShotTask(Task):
489 """A Task that can be run only once. Can schedule itself."""
Chris Masonefad911a2012-03-29 12:30:26 -0700490
491
Chris Masone96f16632012-04-04 18:36:03 -0700492 def Run(self, scheduler, branch_builds, board, force=False):
Chris Masone013859b2012-04-01 13:45:26 -0700493 """Run this task. Returns False, indicating it should be destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -0700494
Chris Masone013859b2012-04-01 13:45:26 -0700495 Run this task. Attempt to schedule the associated suite.
496 Return False, indicating to the caller that it should discard this task.
Chris Masonefad911a2012-03-29 12:30:26 -0700497
498 @param scheduler: an instance of DedupingScheduler, as defined in
499 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -0700500 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -0700501 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -0700502 {'R18': ['x86-alex-release/R18-1655.0.0'],
503 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -0700504 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -0700505 @param force: Always schedule the suite.
506 @return False
507 """
Chris Masone96f16632012-04-04 18:36:03 -0700508 super(OneShotTask, self).Run(scheduler, branch_builds, board, force)
Chris Masonefad911a2012-03-29 12:30:26 -0700509 return False