Home | History | Annotate | Download | only in suite_scheduler
      1 # 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 
      5 import datetime, logging
      6 
      7 import common
      8 from autotest_lib.client.common_lib import priorities
      9 
     10 import base_event, task
     11 
     12 
     13 class TimedEvent(base_event.BaseEvent):
     14     """Base class for events that trigger based on time/day.
     15 
     16     @var _deadline: If this time has passed, ShouldHandle() returns True.
     17     """
     18 
     19 
     20     def __init__(self, keyword, manifest_versions, always_handle, deadline):
     21         """Constructor.
     22 
     23         @param keyword: the keyword/name of this event, e.g. nightly.
     24         @param manifest_versions: ManifestVersions instance to use for querying.
     25         @param always_handle: If True, make ShouldHandle() always return True.
     26         @param deadline: This instance's initial |_deadline|.
     27         """
     28         super(TimedEvent, self).__init__(keyword, manifest_versions,
     29                                          always_handle)
     30         self._deadline = deadline
     31 
     32 
     33     def __ne__(self, other):
     34         return self._deadline != other._deadline or self.tasks != other.tasks
     35 
     36 
     37     def __eq__(self, other):
     38         return self._deadline == other._deadline and self.tasks == other.tasks
     39 
     40 
     41     @staticmethod
     42     def _now():
     43         return datetime.datetime.now()
     44 
     45 
     46     def Prepare(self):
     47         pass
     48 
     49 
     50     def ShouldHandle(self):
     51         """Return True if self._deadline has passed; False if not."""
     52         if super(TimedEvent, self).ShouldHandle():
     53             return True
     54         else:
     55             logging.info('Checking deadline %s for event %s',
     56                          self._deadline, self.keyword)
     57             return self._now() >= self._deadline
     58 
     59 
     60     def _LatestPerBranchBuildsSince(self, board, days_ago):
     61         """Get latest per-branch, per-board builds from last |days_ago| days.
     62 
     63         @param board: the board whose builds we want.
     64         @param days_ago: how many days back to look for manifests.
     65         @return {branch: [build-name]}
     66         """
     67         since_date = self._deadline - datetime.timedelta(days=days_ago)
     68         all_branch_manifests = self._mv.ManifestsSinceDate(since_date, board)
     69         latest_branch_builds = {}
     70         for (type, milestone), manifests in all_branch_manifests.iteritems():
     71             build = base_event.BuildName(board, type, milestone, manifests[-1])
     72             latest_branch_builds[task.PickBranchName(type, milestone)] = [build]
     73         logging.info('%s event found candidate builds: %r',
     74                      self.keyword, latest_branch_builds)
     75         return latest_branch_builds
     76 
     77 
     78 class Nightly(TimedEvent):
     79     """A TimedEvent that allows a task to be triggered at every night. Each task
     80     can set the hour when it should be triggered, through `hour` setting.
     81 
     82     @var KEYWORD: the keyword to use in a run_on option to associate a task
     83                   with the Nightly event.
     84     @var _DEFAULT_HOUR: the default hour to trigger the nightly event.
     85     """
     86 
     87     KEYWORD = 'nightly'
     88     # Each task may have different setting of `hour`. Therefore, nightly tasks
     89     # can run at each hour. The default is set to 9PM.
     90     _DEFAULT_HOUR = 21
     91     PRIORITY = priorities.Priority.DAILY
     92     TIMEOUT = 24  # Kicked off once a day, so they get the full day to run
     93 
     94     def __init__(self, manifest_versions, always_handle):
     95         """Constructor.
     96 
     97         @param manifest_versions: ManifestVersions instance to use for querying.
     98         @param always_handle: If True, make ShouldHandle() always return True.
     99         """
    100         # Set the deadline to the next even hour.
    101         now = self._now()
    102         now_hour = datetime.datetime(now.year, now.month, now.day, now.hour)
    103         extra_hour = 0 if now == now_hour else 1
    104         deadline = now_hour + datetime.timedelta(hours=extra_hour)
    105         super(Nightly, self).__init__(self.KEYWORD, manifest_versions,
    106                                       always_handle, deadline)
    107 
    108 
    109     def GetBranchBuildsForBoard(self, board):
    110         return self._LatestPerBranchBuildsSince(board, 1)
    111 
    112 
    113     def UpdateCriteria(self):
    114         self._deadline = self._deadline + datetime.timedelta(hours=1)
    115 
    116 
    117     def FilterTasks(self):
    118         """Filter the tasks to only return tasks that should run now.
    119 
    120         Nightly task can run at each hour. This function only return the tasks
    121         set to run in current hour.
    122 
    123         @return: A list of tasks that can run now.
    124         """
    125         current_hour = self._now().hour
    126         return [task for task in self.tasks
    127                 if ((task.hour is not None and task.hour == current_hour) or
    128                     (task.hour is None and
    129                      current_hour == self._DEFAULT_HOUR))]
    130 
    131 
    132 class Weekly(TimedEvent):
    133     """A TimedEvent that allows a task to be triggered at every week. Each task
    134     can set the day when it should be triggered, through `day` setting.
    135 
    136     @var KEYWORD: the keyword to use in a run_on option to associate a task
    137                   with the Weekly event.
    138     @var _DEFAULT_DAY: The default day to run a weekly task.
    139     @var _DEFAULT_HOUR: can be overridden in the "weekly_params" config section.
    140     """
    141 
    142     KEYWORD = 'weekly'
    143     _DEFAULT_DAY = 5  # Saturday
    144     _DEFAULT_HOUR = 23
    145     PRIORITY = priorities.Priority.WEEKLY
    146     TIMEOUT = 7 * 24  # 7 days
    147 
    148 
    149     @classmethod
    150     def _ParseConfig(cls, config):
    151         """Create args to pass to __init__ by parsing |config|.
    152 
    153         Calls super class' _ParseConfig() method, then parses these additonal
    154         options:
    155           hour: Integer hour, on a 24 hour clock.
    156         """
    157         from_base = super(Weekly, cls)._ParseConfig(config)
    158 
    159         section = base_event.SectionName(cls.KEYWORD)
    160         event_time = config.getint(section, 'hour') or cls._DEFAULT_HOUR
    161 
    162         from_base.update({'event_time': event_time})
    163         return from_base
    164 
    165 
    166     def __init__(self, manifest_versions, always_handle, event_time):
    167         """Constructor.
    168 
    169         @param manifest_versions: ManifestVersions instance to use for querying.
    170         @param always_handle: If True, make ShouldHandle() always return True.
    171         @param event_time: The hour of the day to set |self._deadline| at.
    172         """
    173         # determine if we're past this week's event and set the
    174         # next deadline for this suite appropriately.
    175         now = self._now()
    176         this_week_deadline = datetime.datetime.combine(
    177                 now, datetime.time(event_time))
    178         if this_week_deadline >= now:
    179             deadline = this_week_deadline
    180         else:
    181             deadline = this_week_deadline + datetime.timedelta(days=1)
    182         super(Weekly, self).__init__(self.KEYWORD, manifest_versions,
    183                                      always_handle, deadline)
    184 
    185 
    186     def Merge(self, to_merge):
    187         """Merge this event with to_merge, changing some mutable properties.
    188 
    189         keyword remains unchanged; the following take on values from to_merge:
    190           _deadline iff the time of day in to_merge._deadline is different.
    191 
    192         @param to_merge: A TimedEvent instance to merge into this instance.
    193         """
    194         super(Weekly, self).Merge(to_merge)
    195         if self._deadline.time() != to_merge._deadline.time():
    196             self._deadline = to_merge._deadline
    197 
    198 
    199     def GetBranchBuildsForBoard(self, board):
    200         return self._LatestPerBranchBuildsSince(board, 7)
    201 
    202 
    203     def UpdateCriteria(self):
    204         self._deadline = self._deadline + datetime.timedelta(days=1)
    205 
    206 
    207     def FilterTasks(self):
    208         """Filter the tasks to only return tasks that should run now.
    209 
    210         Weekly task can be scheduled at any day of the week. This function only
    211         return the tasks set to run in current day.
    212 
    213         @return: A list of tasks that can run now.
    214         """
    215         current_day = self._now().weekday()
    216         return [task for task in self.tasks
    217                 if ((task.day is not None and task.day == current_day) or
    218                     (task.day is None and current_day == self._DEFAULT_DAY))]
    219