blob: 11d5f0e5d6b52aba1ccf8201ce1bb75e01da8437 [file] [log] [blame]
# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import datetime, logging
import common
from autotest_lib.client.common_lib import priorities
import base_event, forgiving_config_parser, task
class TimedEvent(base_event.BaseEvent):
"""Base class for events that trigger based on time/day.
@var _deadline: If this time has passed, ShouldHandle() returns True.
"""
def __init__(self, keyword, manifest_versions, always_handle, deadline):
"""Constructor.
@param keyword: the keyword/name of this event, e.g. nightly.
@param manifest_versions: ManifestVersions instance to use for querying.
@param always_handle: If True, make ShouldHandle() always return True.
@param deadline: This instance's initial |_deadline|.
"""
super(TimedEvent, self).__init__(keyword, manifest_versions,
always_handle)
self._deadline = deadline
def __ne__(self, other):
return self._deadline != other._deadline or self.tasks != other.tasks
def __eq__(self, other):
return self._deadline == other._deadline and self.tasks == other.tasks
@staticmethod
def _now():
return datetime.datetime.now()
def Prepare(self):
pass
def ShouldHandle(self):
"""Return True if self._deadline has passed; False if not."""
if super(TimedEvent, self).ShouldHandle():
return True
else:
logging.info('Checking deadline %s for event %s',
self._deadline, self.keyword)
return self._now() >= self._deadline
def _LatestPerBranchBuildsSince(self, board, days_ago):
"""Get latest per-branch, per-board builds from last |days_ago| days.
@param board: the board whose builds we want.
@param days_ago: how many days back to look for manifests.
@return {branch: [build-name]}
"""
all_branch_manifests = self._mv.ManifestsSinceDays(days_ago, board)
latest_branch_builds = {}
for (type, milestone), manifests in all_branch_manifests.iteritems():
build = base_event.BuildName(board, type, milestone, manifests[-1])
latest_branch_builds[task.PickBranchName(type, milestone)] = [build]
logging.info('%s event found candidate builds: %r',
self.keyword, latest_branch_builds)
return latest_branch_builds
class Nightly(TimedEvent):
"""A TimedEvent that happens every night.
@var KEYWORD: the keyword to use in a run_on option to associate a task
with the Nightly event.
@var _DEFAULT_HOUR: can be overridden in the "nightly_params" config section
"""
KEYWORD = 'nightly'
_DEFAULT_HOUR = 21
PRIORITY = priorities.Priority.DAILY
TIMEOUT = 24 # Kicked off once a day, so they get the full day to run
@classmethod
def _ParseConfig(cls, config):
"""Create args to pass to __init__ by parsing |config|.
Calls super class' _ParseConfig() method, then parses these additonal
options:
hour: Integer hour, on a 24 hour clock.
"""
from_base = super(Nightly, cls)._ParseConfig(config)
section = base_event.SectionName(cls.KEYWORD)
event_time = config.getint(section, 'hour') or cls._DEFAULT_HOUR
from_base.update({'event_time': event_time})
return from_base
def __init__(self, manifest_versions, always_handle, event_time):
"""Constructor.
@param manifest_versions: ManifestVersions instance to use for querying.
@param always_handle: If True, make ShouldHandle() always return True.
@param event_time: The hour of the day to set |self._deadline| at.
"""
# determine if we're past today's nightly event and set the
# next deadline for this suite appropriately.
now = self._now()
tonight = datetime.datetime.combine(now, datetime.time(event_time))
# tonight is now set to today at event_time:00:00
if tonight >= now:
deadline = tonight
else:
deadline = tonight + datetime.timedelta(days=1)
super(Nightly, self).__init__(self.KEYWORD, manifest_versions,
always_handle, deadline)
def Merge(self, to_merge):
"""Merge this event with to_merge, changing some mutable properties.
keyword remains unchanged; the following take on values from to_merge:
_deadline iff the time of day in to_merge._deadline is different.
@param to_merge: A TimedEvent instance to merge into this isntance.
"""
super(Nightly, self).Merge(to_merge)
if self._deadline.time() != to_merge._deadline.time():
self._deadline = to_merge._deadline
def GetBranchBuildsForBoard(self, board):
return self._LatestPerBranchBuildsSince(board, 1)
def UpdateCriteria(self):
self._deadline = self._deadline + datetime.timedelta(days=1)
class Weekly(TimedEvent):
"""A TimedEvent that happens every week.
@var KEYWORD: the keyword to use in a run_on option to associate a task
with the Weekly event.
@var _DEFAULT_DAY: can be overridden in the "weekly_params" config section.
@var _DEFAULT_HOUR: can be overridden in the "weekly_params" config section.
"""
KEYWORD = 'weekly'
_DEFAULT_DAY = 5 # Saturday
_DEFAULT_HOUR = 23
PRIORITY = priorities.Priority.WEEKLY
TIMEOUT = 7 * 24 # 7 days
@classmethod
def _ParseConfig(cls, config):
"""Create args to pass to __init__ by parsing |config|.
Calls super class' _ParseConfig() method, then parses these additonal
options:
hour: Integer hour, on a 24 hour clock.
day: Integer day, in a 0-indexed 7 day week, e.g. 5 == Saturday.
"""
from_base = super(Weekly, cls)._ParseConfig(config)
section = base_event.SectionName(cls.KEYWORD)
event_time = config.getint(section, 'hour') or cls._DEFAULT_HOUR
event_day = config.getint(section, 'day') or cls._DEFAULT_DAY
from_base.update({'event_time': event_time, 'event_day': event_day})
return from_base
def __init__(self, manifest_versions, always_handle, event_day, event_time):
"""Constructor.
@param manifest_versions: ManifestVersions instance to use for querying.
@param always_handle: If True, make ShouldHandle() always return True.
@param event_day: The day of the week to set |self._deadline| at.
@param event_time: The hour of the day to set |self._deadline| at.
"""
# determine if we're past this week's event and set the
# next deadline for this suite appropriately.
now = self._now()
# Get a datetime representing this week's event_day
# If now() is a Sunday, we 'add' 5 - 6 = -1 days to go back a day.
# If now() is a Monday, we add 5 - 0 = 5 days to jump forward.
this_week = now + datetime.timedelta(event_day-now.weekday())
this_week_deadline = datetime.datetime.combine(
this_week, datetime.time(event_time))
if this_week_deadline >= now:
deadline = this_week_deadline
else:
deadline = this_week_deadline + datetime.timedelta(days=7)
super(Weekly, self).__init__(self.KEYWORD, manifest_versions,
always_handle, deadline)
def Merge(self, to_merge):
"""Merge this event with to_merge, changing some mutable properties.
keyword remains unchanged; the following take on values from to_merge:
_deadline iff the time of day in to_merge._deadline is different.
@param to_merge: A TimedEvent instance to merge into this isntance.
"""
super(Weekly, self).Merge(to_merge)
if (self._deadline.time() != to_merge._deadline.time() or
self._deadline.weekday() != to_merge._deadline.weekday()):
self._deadline = to_merge._deadline
def GetBranchBuildsForBoard(self, board):
return self._LatestPerBranchBuildsSince(board, 7)
def UpdateCriteria(self):
self._deadline = self._deadline + datetime.timedelta(days=7)