blob: a4a54d379f45e4747402868b3dc67c375b76f845 [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
Aviv Keshetd83ef442013-01-16 16:19:35 -08009import deduping_scheduler
Alex Millerc7bcf8b2013-09-07 20:13:20 -070010import driver
Chris Masone67f06d62012-04-12 15:16:56 -070011from distutils import version
Alex Miller511a9e32012-07-03 09:16:47 -070012from constants import Labels
Chris Masone96f16632012-04-04 18:36:03 -070013
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070014import common
15from autotest_lib.server.cros.dynamic_suite import constants
16from autotest_lib.scheduler import scheduler_lib
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070017
Chris Masone96f16632012-04-04 18:36:03 -070018
19class MalformedConfigEntry(Exception):
20 """Raised to indicate a failure to parse a Task out of a config."""
21 pass
Chris Masonefad911a2012-03-29 12:30:26 -070022
23
Alex Miller0d003572013-03-18 11:51:30 -070024BARE_BRANCHES = ['factory', 'firmware']
Chris Masone67f06d62012-04-12 15:16:56 -070025
26
27def PickBranchName(type, milestone):
Dan Shiaceb91d2013-02-20 12:41:28 -080028 """Pick branch name. If type is among BARE_BRANCHES, return type,
29 otherwise, return milestone.
30
31 @param type: type of the branch, e.g., 'release', 'factory', or 'firmware'
32 @param milestone: CrOS milestone number
33 """
Chris Masone67f06d62012-04-12 15:16:56 -070034 if type in BARE_BRANCHES:
35 return type
36 return milestone
37
38
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070039class TotMilestoneManager(object):
40 """A class capable of converting tot string to milestone numbers.
41
42 This class is used as a cache for the tot milestone, so we don't
43 repeatedly hit google storage for all O(100) tasks in suite
44 scheduler's ini file.
45 """
46
47 __metaclass__ = scheduler_lib.Singleton
48
Dan Shi23245142015-01-22 13:22:28 -080049 # True if suite_scheduler is running for sanity check. When it's set to
50 # True, the code won't make gsutil call to get the actual tot milestone to
51 # avoid dependency on the installation of gsutil to run sanity check.
52 is_sanity = False
53
54
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070055 @staticmethod
56 def _tot_milestone():
57 """Get the tot milestone, eg: R40
58
59 @returns: A string representing the Tot milestone as declared by
60 the LATEST_BUILD_URL, or an empty string if LATEST_BUILD_URL
61 doesn't exist.
62 """
Dan Shi23245142015-01-22 13:22:28 -080063 if TotMilestoneManager.is_sanity:
64 logging.info('suite_scheduler is running for sanity purpose, no '
65 'need to get the actual tot milestone string.')
66 return 'R40'
67
Prashanth Balasubramanian1b859622014-10-28 16:02:15 -070068 cmd = ['gsutil', 'cat', constants.LATEST_BUILD_URL]
69 proc = subprocess.Popen(
70 cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
71 stdout, stderr = proc.communicate()
72 if proc.poll():
73 logging.warning('Failed to get latest build: %s', stderr)
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070074 return ''
Prashanth Balasubramanian1b859622014-10-28 16:02:15 -070075 return stdout.split('-')[0]
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -070076
77
78 def refresh(self):
79 """Refresh the tot milestone string managed by this class."""
80 self.tot = self._tot_milestone()
81
82
83 def __init__(self):
84 """Initialize a TotMilestoneManager."""
85 self.refresh()
86
87
88 def ConvertTotSpec(self, tot_spec):
89 """Converts a tot spec to the appropriate milestone.
90
91 Assume tot is R40:
92 tot -> R40
93 tot-1 -> R39
94 tot-2 -> R38
95 tot-(any other numbers) -> R40
96
97 With the last option one assumes that a malformed configuration that has
98 'tot' in it, wants at least tot.
99
100 @param tot_spec: A string representing the tot spec.
101 @raises MalformedConfigEntry: If the tot_spec doesn't match the
102 expected format.
103 """
104 tot_spec = tot_spec.lower()
105 match = re.match('(tot)[-]?(1$|2$)?', tot_spec)
106 if not match:
107 raise MalformedConfigEntry(
108 "%s isn't a valid branch spec." % tot_spec)
109 tot_mstone = self.tot
110 num_back = match.groups()[1]
111 if num_back:
112 tot_mstone_num = tot_mstone.lstrip('R')
113 tot_mstone = tot_mstone.replace(
114 tot_mstone_num, str(int(tot_mstone_num)-int(num_back)))
115 return tot_mstone
116
117
Chris Masone013859b2012-04-01 13:45:26 -0700118class Task(object):
Chris Masonefad911a2012-03-29 12:30:26 -0700119 """Represents an entry from the scheduler config. Can schedule itself.
120
121 Each entry from the scheduler config file maps one-to-one to a
Chris Masone013859b2012-04-01 13:45:26 -0700122 Task. Each instance has enough info to schedule itself
Chris Masonefad911a2012-03-29 12:30:26 -0700123 on-demand with the AFE.
124
125 This class also overrides __hash__() and all comparitor methods to enable
126 correct use in dicts, sets, etc.
127 """
128
Chris Masone96f16632012-04-04 18:36:03 -0700129
130 @staticmethod
131 def CreateFromConfigSection(config, section):
132 """Create a Task from a section of a config file.
133
134 The section to parse should look like this:
135 [TaskName]
136 suite: suite_to_run # Required
137 run_on: event_on which to run # Required
Dan Shiaceb91d2013-02-20 12:41:28 -0800138 branch_specs: factory,firmware,>=R12 or ==R12 # Optional
Chris Masone96f16632012-04-04 18:36:03 -0700139 pool: pool_of_devices # Optional
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700140 num: sharding_factor # int, Optional
Alex Millerf2b57442013-09-07 18:40:02 -0700141 boards: board1, board2 # comma seperated string, Optional
Chris Masone96f16632012-04-04 18:36:03 -0700142
Chris Masone67f06d62012-04-12 15:16:56 -0700143 By default, Tasks run on all release branches, not factory or firmware.
144
Chris Masone96f16632012-04-04 18:36:03 -0700145 @param config: a ForgivingConfigParser.
146 @param section: the section to parse into a Task.
147 @return keyword, Task object pair. One or both will be None on error.
148 @raise MalformedConfigEntry if there's a problem parsing |section|.
149 """
Alex Miller06695022012-07-18 09:31:36 -0700150 if not config.has_section(section):
151 raise MalformedConfigEntry('unknown section %s' % section)
152
Alex Millerf2b57442013-09-07 18:40:02 -0700153 allowed = set(['suite', 'run_on', 'branch_specs', 'pool', 'num',
Prashanth B6de2bde2014-03-25 18:45:02 -0700154 'boards', 'file_bugs'])
Alex Millerbb535e22012-07-11 20:11:33 -0700155 # The parameter of union() is the keys under the section in the config
156 # The union merges this with the allowed set, so if any optional keys
157 # are omitted, then they're filled in. If any extra keys are present,
158 # then they will expand unioned set, causing it to fail the following
159 # comparison against the allowed set.
160 section_headers = allowed.union(dict(config.items(section)).keys())
161 if allowed != section_headers:
162 raise MalformedConfigEntry('unknown entries: %s' %
163 ", ".join(map(str, section_headers.difference(allowed))))
164
Chris Masone96f16632012-04-04 18:36:03 -0700165 keyword = config.getstring(section, 'run_on')
166 suite = config.getstring(section, 'suite')
167 branches = config.getstring(section, 'branch_specs')
168 pool = config.getstring(section, 'pool')
Alex Millerf2b57442013-09-07 18:40:02 -0700169 boards = config.getstring(section, 'boards')
Prashanth B6de2bde2014-03-25 18:45:02 -0700170 file_bugs = config.getboolean(section, 'file_bugs')
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700171 for klass in driver.Driver.EVENT_CLASSES:
172 if klass.KEYWORD == keyword:
173 priority = klass.PRIORITY
174 timeout = klass.TIMEOUT
175 break
176 else:
177 priority = None
178 timeout = None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700179 try:
180 num = config.getint(section, 'num')
181 except ValueError as e:
182 raise MalformedConfigEntry("Ill-specified 'num': %r" %e)
Chris Masone96f16632012-04-04 18:36:03 -0700183 if not keyword:
184 raise MalformedConfigEntry('No event to |run_on|.')
185 if not suite:
186 raise MalformedConfigEntry('No |suite|')
187 specs = []
188 if branches:
189 specs = re.split('\s*,\s*', branches)
190 Task.CheckBranchSpecs(specs)
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700191 return keyword, Task(section, suite, specs, pool, num, boards,
Prashanth B6de2bde2014-03-25 18:45:02 -0700192 priority, timeout,
193 file_bugs=file_bugs if file_bugs else False)
Chris Masone96f16632012-04-04 18:36:03 -0700194
195
196 @staticmethod
197 def CheckBranchSpecs(branch_specs):
198 """Make sure entries in the list branch_specs are correctly formed.
199
Chris Masone67f06d62012-04-12 15:16:56 -0700200 We accept any of BARE_BRANCHES in |branch_specs|, as
Dan Shiaceb91d2013-02-20 12:41:28 -0800201 well as _one_ string of the form '>=RXX' or '==RXX', where 'RXX' is a
Chris Masone96f16632012-04-04 18:36:03 -0700202 CrOS milestone number.
203
204 @param branch_specs: an iterable of branch specifiers.
205 @raise MalformedConfigEntry if there's a problem parsing |branch_specs|.
206 """
207 have_seen_numeric_constraint = False
208 for branch in branch_specs:
Chris Masone67f06d62012-04-12 15:16:56 -0700209 if branch in BARE_BRANCHES:
Chris Masone96f16632012-04-04 18:36:03 -0700210 continue
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700211 if not have_seen_numeric_constraint:
212 #TODO(beeps): Why was <= dropped on the floor?
213 if branch.startswith('>=R') or branch.startswith('==R'):
214 have_seen_numeric_constraint = True
215 elif 'tot' in branch:
216 TotMilestoneManager().ConvertTotSpec(
217 branch[branch.index('tot'):])
218 have_seen_numeric_constraint = True
Chris Masone96f16632012-04-04 18:36:03 -0700219 continue
Chris Masone97cf0a72012-05-16 09:55:52 -0700220 raise MalformedConfigEntry("%s isn't a valid branch spec." % branch)
Chris Masone96f16632012-04-04 18:36:03 -0700221
222
Alex Millerf2b57442013-09-07 18:40:02 -0700223 def __init__(self, name, suite, branch_specs, pool=None, num=None,
Prashanth B6de2bde2014-03-25 18:45:02 -0700224 boards=None, priority=None, timeout=None, file_bugs=False):
Chris Masonefad911a2012-03-29 12:30:26 -0700225 """Constructor
226
Chris Masone96f16632012-04-04 18:36:03 -0700227 Given an iterable in |branch_specs|, pre-vetted using CheckBranchSpecs,
228 we'll store them such that _FitsSpec() can be used to check whether a
229 given branch 'fits' with the specifications passed in here.
230 For example, given branch_specs = ['factory', '>=R18'], we'd set things
231 up so that _FitsSpec() would return True for 'factory', or 'RXX'
Dan Shiaceb91d2013-02-20 12:41:28 -0800232 where XX is a number >= 18. Same check is done for branch_specs = [
233 'factory', '==R18'], which limit the test to only one specific branch.
Chris Masone96f16632012-04-04 18:36:03 -0700234
235 Given branch_specs = ['factory', 'firmware'], _FitsSpec()
236 would pass only those two specific strings.
237
238 Example usage:
Chris Masonecc4631d2012-04-20 12:06:39 -0700239 t = Task('Name', 'suite', ['factory', '>=R18'])
Chris Masone96f16632012-04-04 18:36:03 -0700240 t._FitsSpec('factory') # True
241 t._FitsSpec('R19') # True
242 t._FitsSpec('R17') # False
243 t._FitsSpec('firmware') # False
244 t._FitsSpec('goober') # False
245
Dan Shiaceb91d2013-02-20 12:41:28 -0800246 t = Task('Name', 'suite', ['factory', '==R18'])
247 t._FitsSpec('R19') # False, branch does not equal to 18
248 t._FitsSpec('R18') # True
249 t._FitsSpec('R17') # False
250
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700251 @param name: name of this task, e.g. 'NightlyPower'
Chris Masonefad911a2012-03-29 12:30:26 -0700252 @param suite: the name of the suite to run, e.g. 'bvt'
Chris Masone96f16632012-04-04 18:36:03 -0700253 @param branch_specs: a pre-vetted iterable of branch specifiers,
254 e.g. ['>=R18', 'factory']
Chris Masonefad911a2012-03-29 12:30:26 -0700255 @param pool: the pool of machines to use for scheduling purposes.
256 Default: None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700257 @param num: the number of devices across which to shard the test suite.
Aviv Keshetd83ef442013-01-16 16:19:35 -0800258 Type: integer or None
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700259 Default: None
Alex Millerf2b57442013-09-07 18:40:02 -0700260 @param boards: A comma seperated list of boards to run this task on.
261 Default: Run on all boards.
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700262 @param priority: The string name of a priority from
263 client.common_lib.priorities.Priority.
264 @param timeout: The max lifetime of the suite in hours.
Prashanth B6de2bde2014-03-25 18:45:02 -0700265 @param file_bugs: True if bug filing is desired for the suite created
266 for this task.
Chris Masonefad911a2012-03-29 12:30:26 -0700267 """
Chris Masonecc4631d2012-04-20 12:06:39 -0700268 self._name = name
Chris Masonefad911a2012-03-29 12:30:26 -0700269 self._suite = suite
Chris Masone96f16632012-04-04 18:36:03 -0700270 self._branch_specs = branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700271 self._pool = pool
Aviv Keshetd83ef442013-01-16 16:19:35 -0800272 self._num = num
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700273 self._priority = priority
274 self._timeout = timeout
Prashanth B6de2bde2014-03-25 18:45:02 -0700275 self._file_bugs = file_bugs
Chris Masone96f16632012-04-04 18:36:03 -0700276
277 self._bare_branches = []
Dan Shiaceb91d2013-02-20 12:41:28 -0800278 self._version_equal_constraint = False
Chris Masone67f06d62012-04-12 15:16:56 -0700279 if not branch_specs:
280 # Any milestone is OK.
281 self._numeric_constraint = version.LooseVersion('0')
282 else:
283 self._numeric_constraint = None
284 for spec in branch_specs:
Prashanth Balasubramanianf571aa62014-10-13 18:09:44 -0700285 if 'tot' in spec.lower():
286 tot_str = spec[spec.index('tot'):]
287 spec = spec.replace(
288 tot_str, TotMilestoneManager().ConvertTotSpec(
289 tot_str))
Chris Masone67f06d62012-04-12 15:16:56 -0700290 if spec.startswith('>='):
291 self._numeric_constraint = version.LooseVersion(
292 spec.lstrip('>=R'))
Dan Shiaceb91d2013-02-20 12:41:28 -0800293 elif spec.startswith('=='):
294 self._version_equal_constraint = True
295 self._numeric_constraint = version.LooseVersion(
296 spec.lstrip('==R'))
Chris Masone67f06d62012-04-12 15:16:56 -0700297 else:
298 self._bare_branches.append(spec)
Alex Millerf2b57442013-09-07 18:40:02 -0700299
Chris Masonefad911a2012-03-29 12:30:26 -0700300 # Since we expect __hash__() and other comparitor methods to be used
301 # frequently by set operations, and they use str() a lot, pre-compute
302 # the string representation of this object.
Aviv Keshet8ce3c982013-01-24 09:23:13 -0800303 if num is None:
304 numStr = '[Default num]'
305 else:
306 numStr = '%d' % num
Alex Millerf2b57442013-09-07 18:40:02 -0700307
308 if boards is None:
309 self._boards = set()
310 boardsStr = '[All boards]'
311 else:
312 self._boards = set([x.strip() for x in boards.split(',')])
313 boardsStr = boards
314
Prashanth B6de2bde2014-03-25 18:45:02 -0700315 self._str = ('%s: %s on %s with pool %s, boards [%s], file_bugs = %s '
316 'across %s machines.' % (self.__class__.__name__,
317 suite, branch_specs, pool, boardsStr, self._file_bugs,
318 numStr))
Chris Masone96f16632012-04-04 18:36:03 -0700319
320
321 def _FitsSpec(self, branch):
322 """Checks if a branch is deemed OK by this instance's branch specs.
323
324 When called on a branch name, will return whether that branch
Dan Shiaceb91d2013-02-20 12:41:28 -0800325 'fits' the specifications stored in self._bare_branches,
326 self._numeric_constraint and self._version_equal_constraint.
Chris Masone96f16632012-04-04 18:36:03 -0700327
328 @param branch: the branch to check.
329 @return True if b 'fits' with stored specs, False otherwise.
330 """
Chris Masone657e1552012-05-30 17:06:20 -0700331 if branch in BARE_BRANCHES:
332 return branch in self._bare_branches
Dan Shiaceb91d2013-02-20 12:41:28 -0800333 if self._numeric_constraint:
334 if self._version_equal_constraint:
335 return version.LooseVersion(branch) == self._numeric_constraint
336 else:
337 return version.LooseVersion(branch) >= self._numeric_constraint
338 else:
339 return False
Chris Masonefad911a2012-03-29 12:30:26 -0700340
341
342 @property
Alex Miller9979b5a2012-11-01 17:36:12 -0700343 def name(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800344 """Name of this task, e.g. 'NightlyPower'."""
Alex Miller9979b5a2012-11-01 17:36:12 -0700345 return self._name
346
347
348 @property
Chris Masonefad911a2012-03-29 12:30:26 -0700349 def suite(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800350 """Name of the suite to run, e.g. 'bvt'."""
Chris Masonefad911a2012-03-29 12:30:26 -0700351 return self._suite
352
353
354 @property
Chris Masone96f16632012-04-04 18:36:03 -0700355 def branch_specs(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800356 """a pre-vetted iterable of branch specifiers,
357 e.g. ['>=R18', 'factory']."""
Chris Masone96f16632012-04-04 18:36:03 -0700358 return self._branch_specs
Chris Masonefad911a2012-03-29 12:30:26 -0700359
360
361 @property
Chris Masone3fba86f2012-04-03 10:06:56 -0700362 def pool(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800363 """The pool of machines to use for scheduling purposes."""
Chris Masone3fba86f2012-04-03 10:06:56 -0700364 return self._pool
Chris Masonefad911a2012-03-29 12:30:26 -0700365
366
Alex Miller9979b5a2012-11-01 17:36:12 -0700367 @property
368 def num(self):
Dan Shiaceb91d2013-02-20 12:41:28 -0800369 """The number of devices across which to shard the test suite.
370 Type: integer or None"""
Alex Miller9979b5a2012-11-01 17:36:12 -0700371 return self._num
372
373
Alex Millerf2b57442013-09-07 18:40:02 -0700374 @property
375 def boards(self):
376 """The boards on which to run this suite.
377 Type: Iterable of strings"""
378 return self._boards
379
380
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700381 @property
382 def priority(self):
383 """The priority of the suite"""
384 return self._priority
385
386
387 @property
388 def timeout(self):
389 """The maximum lifetime of the suite in hours."""
390 return self._timeout
391
392
Chris Masonefad911a2012-03-29 12:30:26 -0700393 def __str__(self):
394 return self._str
395
396
Chris Masone3eeaf0a2012-08-09 14:07:27 -0700397 def __repr__(self):
398 return self._str
399
400
Chris Masonefad911a2012-03-29 12:30:26 -0700401 def __lt__(self, other):
402 return str(self) < str(other)
403
404
405 def __le__(self, other):
406 return str(self) <= str(other)
407
408
409 def __eq__(self, other):
410 return str(self) == str(other)
411
412
413 def __ne__(self, other):
414 return str(self) != str(other)
415
416
417 def __gt__(self, other):
418 return str(self) > str(other)
419
420
421 def __ge__(self, other):
422 return str(self) >= str(other)
423
424
425 def __hash__(self):
426 """Allows instances to be correctly deduped when used in a set."""
427 return hash(str(self))
428
429
Alex Miller7ce1b362012-07-10 09:24:10 -0700430 def AvailableHosts(self, scheduler, board):
431 """Query what hosts are able to run a test on a board and pool
432 combination.
Alex Miller511a9e32012-07-03 09:16:47 -0700433
434 @param scheduler: an instance of DedupingScheduler, as defined in
435 deduping_scheduler.py
436 @param board: the board against which one wants to run the test.
Alex Miller7ce1b362012-07-10 09:24:10 -0700437 @return The list of hosts meeting the board and pool requirements,
438 or None if no hosts were found."""
Alex Millerf2b57442013-09-07 18:40:02 -0700439 if self._boards and board not in self._boards:
440 return []
441
Alex Miller511a9e32012-07-03 09:16:47 -0700442 labels = [Labels.BOARD_PREFIX + board]
443 if self._pool:
Chris Masonecd214e02012-07-10 16:22:10 -0700444 labels.append(Labels.POOL_PREFIX + self._pool)
Alex Miller511a9e32012-07-03 09:16:47 -0700445
Prashanth B6de2bde2014-03-25 18:45:02 -0700446 return scheduler.CheckHostsExist(multiple_labels=labels)
Alex Miller511a9e32012-07-03 09:16:47 -0700447
448
Alex Millerd621cf22012-07-11 13:57:10 -0700449 def ShouldHaveAvailableHosts(self):
450 """As a sanity check, return true if we know for certain that
451 we should be able to schedule this test. If we claim this test
452 should be able to run, and it ends up not being scheduled, then
453 a warning will be reported.
454
455 @return True if this test should be able to run, False otherwise.
456 """
457 return self._pool == 'bvt'
458
459
Chris Masone96f16632012-04-04 18:36:03 -0700460 def Run(self, scheduler, branch_builds, board, force=False):
Chris Masone013859b2012-04-01 13:45:26 -0700461 """Run this task. Returns False if it should be destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -0700462
Chris Masone013859b2012-04-01 13:45:26 -0700463 Execute this task. Attempt to schedule the associated suite.
464 Return True if this task should be kept around, False if it
465 should be destroyed. This allows for one-shot Tasks.
Chris Masonefad911a2012-03-29 12:30:26 -0700466
467 @param scheduler: an instance of DedupingScheduler, as defined in
468 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -0700469 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -0700470 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -0700471 {'R18': ['x86-alex-release/R18-1655.0.0'],
472 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -0700473 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -0700474 @param force: Always schedule the suite.
Chris Masone013859b2012-04-01 13:45:26 -0700475 @return True if the task should be kept, False if not
Chris Masonefad911a2012-03-29 12:30:26 -0700476 """
Chris Masonea3a38172012-05-14 15:19:56 -0700477 logging.info('Running %s on %s', self._name, board)
Chris Masone96f16632012-04-04 18:36:03 -0700478 builds = []
Chris Masonefe5a5092012-04-11 18:29:07 -0700479 for branch, build in branch_builds.iteritems():
Chris Masonea3a38172012-05-14 15:19:56 -0700480 logging.info('Checking if %s fits spec %r',
481 branch, self.branch_specs)
Chris Masone96f16632012-04-04 18:36:03 -0700482 if self._FitsSpec(branch):
Chris Masone05b19442012-04-17 13:37:55 -0700483 builds.extend(build)
Chris Masone96f16632012-04-04 18:36:03 -0700484 for build in builds:
Chris Masone3fba86f2012-04-03 10:06:56 -0700485 try:
Chris Masone96f16632012-04-04 18:36:03 -0700486 if not scheduler.ScheduleSuite(self._suite, board, build,
Alex Millerc7bcf8b2013-09-07 20:13:20 -0700487 self._pool, self._num,
488 self._priority, self._timeout,
Prashanth B6de2bde2014-03-25 18:45:02 -0700489 force,
490 file_bugs=self._file_bugs):
Chris Masone96f16632012-04-04 18:36:03 -0700491 logging.info('Skipping scheduling %s on %s for %s',
492 self._suite, build, board)
Chris Masone3fba86f2012-04-03 10:06:56 -0700493 except deduping_scheduler.DedupingSchedulerException as e:
494 logging.error(e)
Chris Masonefad911a2012-03-29 12:30:26 -0700495 return True
496
497
Chris Masone013859b2012-04-01 13:45:26 -0700498class OneShotTask(Task):
499 """A Task that can be run only once. Can schedule itself."""
Chris Masonefad911a2012-03-29 12:30:26 -0700500
501
Chris Masone96f16632012-04-04 18:36:03 -0700502 def Run(self, scheduler, branch_builds, board, force=False):
Chris Masone013859b2012-04-01 13:45:26 -0700503 """Run this task. Returns False, indicating it should be destroyed.
Chris Masonefad911a2012-03-29 12:30:26 -0700504
Chris Masone013859b2012-04-01 13:45:26 -0700505 Run this task. Attempt to schedule the associated suite.
506 Return False, indicating to the caller that it should discard this task.
Chris Masonefad911a2012-03-29 12:30:26 -0700507
508 @param scheduler: an instance of DedupingScheduler, as defined in
509 deduping_scheduler.py
Chris Masone05b19442012-04-17 13:37:55 -0700510 @param branch_builds: a dict mapping branch name to the build(s) to
Chris Masone96f16632012-04-04 18:36:03 -0700511 install for that branch, e.g.
Chris Masone05b19442012-04-17 13:37:55 -0700512 {'R18': ['x86-alex-release/R18-1655.0.0'],
513 'R19': ['x86-alex-release/R19-2077.0.0']}
Chris Masone96f16632012-04-04 18:36:03 -0700514 @param board: the board against which to run self._suite.
Chris Masonefad911a2012-03-29 12:30:26 -0700515 @param force: Always schedule the suite.
516 @return False
517 """
Chris Masone96f16632012-04-04 18:36:03 -0700518 super(OneShotTask, self).Run(scheduler, branch_builds, board, force)
Chris Masonefad911a2012-03-29 12:30:26 -0700519 return False