12d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone# Copyright (c) 2012 The Chromium OS Authors. All rights reserved. 22d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone# Use of this source code is governed by a BSD-style license that can be 32d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone# found in the LICENSE file. 42d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 53197b39f82eb92afff33c7d44b805afe120c7627Fang Dengimport logging 6c7bcf8bf70d6e40aa4d37528f75b5a98b0f7a00eAlex Miller 7c7bcf8bf70d6e40aa4d37528f75b5a98b0f7a00eAlex Millerimport common 8c7bcf8bf70d6e40aa4d37528f75b5a98b0f7a00eAlex Millerfrom autotest_lib.client.common_lib import priorities 9c7bcf8bf70d6e40aa4d37528f75b5a98b0f7a00eAlex Miller 102d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 1193f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone"""Module containing base class and methods for working with scheduler events. 1293f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone 1393f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone@var _SECTION_SUFFIX: suffix of config file sections that apply to derived 1493f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone classes of TimedEvent. 1593f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone""" 1693f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone 1793f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone 1893f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone_SECTION_SUFFIX = '_params' 1993f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone 202d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 2193f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masonedef SectionName(keyword): 2293f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone """Generate a section name for a *Event config stanza.""" 2393f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone return keyword + _SECTION_SUFFIX 2493f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone 2593f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone 2693f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masonedef HonoredSection(section): 2793f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone """Returns True if section is something _ParseConfig() might consume.""" 2893f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone return section.endswith(_SECTION_SUFFIX) 2993f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone 3093f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone 3167f06d6003bb11f03f925810272f595d79dce44aChris Masonedef BuildName(board, type, milestone, manifest): 3267f06d6003bb11f03f925810272f595d79dce44aChris Masone """Format a build name, given board, type, milestone, and manifest number. 3367f06d6003bb11f03f925810272f595d79dce44aChris Masone 3467f06d6003bb11f03f925810272f595d79dce44aChris Masone @param board: board the manifest is for, e.g. x86-alex. 3567f06d6003bb11f03f925810272f595d79dce44aChris Masone @param type: one of 'release', 'factory', or 'firmware' 3667f06d6003bb11f03f925810272f595d79dce44aChris Masone @param milestone: (numeric) milestone the manifest was associated with. 3767f06d6003bb11f03f925810272f595d79dce44aChris Masone @param manifest: manifest number, e.g. '2015.0.0' 3867f06d6003bb11f03f925810272f595d79dce44aChris Masone @return a build name, e.g. 'x86-alex-release/R20-2015.0.0' 3967f06d6003bb11f03f925810272f595d79dce44aChris Masone """ 4067f06d6003bb11f03f925810272f595d79dce44aChris Masone return '%s-%s/R%s-%s' % (board, type, milestone, manifest) 4167f06d6003bb11f03f925810272f595d79dce44aChris Masone 4267f06d6003bb11f03f925810272f595d79dce44aChris Masone 432d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masoneclass BaseEvent(object): 442d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone """Represents a supported scheduler event. 452d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 46c7bcf8bf70d6e40aa4d37528f75b5a98b0f7a00eAlex Miller @var PRIORITY: The priority of suites kicked off by this event. 47c7bcf8bf70d6e40aa4d37528f75b5a98b0f7a00eAlex Miller @var TIMEOUT: The max lifetime of suites kicked off by this event. 48c7bcf8bf70d6e40aa4d37528f75b5a98b0f7a00eAlex Miller 492d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone @var _keyword: the keyword/name of this event, e.g. new_build, nightly. 5093f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone @var _mv: ManifestVersions instance used to query for new builds, etc. 51855d86f838faccb4ad112a222412e29c047b7d3eChris Masone @var _always_handle: whether to make ShouldHandle always return True. 522d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone @var _tasks: set of Task instances that run on this event. 532d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone Use a set so that instances that encode logically equivalent 542d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone Tasks get de-duped before we even try to schedule them. 552d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone """ 562d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 572d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 58c7bcf8bf70d6e40aa4d37528f75b5a98b0f7a00eAlex Miller PRIORITY = priorities.Priority.DEFAULT 59c7bcf8bf70d6e40aa4d37528f75b5a98b0f7a00eAlex Miller TIMEOUT = 24 # Hours 60c7bcf8bf70d6e40aa4d37528f75b5a98b0f7a00eAlex Miller 61c7bcf8bf70d6e40aa4d37528f75b5a98b0f7a00eAlex Miller 6296f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone @classmethod 6393f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone def CreateFromConfig(cls, config, manifest_versions): 6496f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone """Instantiate a cls object, options from |config|.""" 6593f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone return cls(manifest_versions, **cls._ParseConfig(config)) 6696f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone 6796f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone 6896f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone @classmethod 6996f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone def _ParseConfig(cls, config): 70fe5a509f533e7de2531a0d0838146859b543569cChris Masone """Parse config and return a dict of parameters for this event. 71fe5a509f533e7de2531a0d0838146859b543569cChris Masone 7293f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone Uses cls.KEYWORD to determine which section to look at, and parses 7393f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone the following options: 7493f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone always_handle: If True, ShouldHandle() must always return True. 7593f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone 7693f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone @param config: a ForgivingConfigParser instance. 77fe5a509f533e7de2531a0d0838146859b543569cChris Masone """ 7893f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone section = SectionName(cls.KEYWORD) 7993f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone return {'always_handle': config.getboolean(section, 'always_handle')} 8096f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone 8196f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone 8293f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone def __init__(self, keyword, manifest_versions, always_handle): 832d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone """Constructor. 842d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 852d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone @param keyword: the keyword/name of this event, e.g. nightly. 8693f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone @param manifest_versions: ManifestVersions instance to use for querying. 8793f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone @param always_handle: If True, make ShouldHandle() always return True. 882d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone """ 892d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone self._keyword = keyword 9093f51d4da152538007d5b44a2dc9d2bbb1fe3429Chris Masone self._mv = manifest_versions 9196f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone self._tasks = set() 92855d86f838faccb4ad112a222412e29c047b7d3eChris Masone self._always_handle = always_handle 932d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 942d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 952d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone @property 962d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone def keyword(self): 972d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone """Getter for private |self._keyword| property.""" 982d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone return self._keyword 992d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 1002d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 10196f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone @property 10296f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone def tasks(self): 10396f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone return self._tasks 10496f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone 10596f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone 10696f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone @tasks.setter 10796f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone def tasks(self, iterable_of_tasks): 10896f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone """Set the tasks property with an iterable. 10996f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone 11096f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone @param iterable_of_tasks: list of Task instances that can fire on this. 11196f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone """ 11296f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone self._tasks = set(iterable_of_tasks) 11396f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone 11496f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone 115855d86f838faccb4ad112a222412e29c047b7d3eChris Masone def Merge(self, to_merge): 116855d86f838faccb4ad112a222412e29c047b7d3eChris Masone """Merge this event with to_merge, changing all mutable properties. 117855d86f838faccb4ad112a222412e29c047b7d3eChris Masone 118855d86f838faccb4ad112a222412e29c047b7d3eChris Masone keyword remains unchanged; the following take on values from to_merge: 119855d86f838faccb4ad112a222412e29c047b7d3eChris Masone _tasks 120855d86f838faccb4ad112a222412e29c047b7d3eChris Masone _mv 121855d86f838faccb4ad112a222412e29c047b7d3eChris Masone _always_handle 122855d86f838faccb4ad112a222412e29c047b7d3eChris Masone 123855d86f838faccb4ad112a222412e29c047b7d3eChris Masone @param to_merge: A BaseEvent instance to merge into this instance. 124855d86f838faccb4ad112a222412e29c047b7d3eChris Masone """ 125855d86f838faccb4ad112a222412e29c047b7d3eChris Masone self.tasks = to_merge.tasks 126855d86f838faccb4ad112a222412e29c047b7d3eChris Masone self._mv = to_merge._mv 127855d86f838faccb4ad112a222412e29c047b7d3eChris Masone self._always_handle = to_merge._always_handle 128855d86f838faccb4ad112a222412e29c047b7d3eChris Masone 129855d86f838faccb4ad112a222412e29c047b7d3eChris Masone 13073a7838db228a1b4a6141b5ba9efa4d1d1cf6251Chris Masone def Prepare(self): 13173a7838db228a1b4a6141b5ba9efa4d1d1cf6251Chris Masone """Perform any one-time setup that must occur before [Should]Handle(). 13273a7838db228a1b4a6141b5ba9efa4d1d1cf6251Chris Masone 13373a7838db228a1b4a6141b5ba9efa4d1d1cf6251Chris Masone Must be implemented by subclasses. 13473a7838db228a1b4a6141b5ba9efa4d1d1cf6251Chris Masone """ 13573a7838db228a1b4a6141b5ba9efa4d1d1cf6251Chris Masone raise NotImplementedError() 13673a7838db228a1b4a6141b5ba9efa4d1d1cf6251Chris Masone 13773a7838db228a1b4a6141b5ba9efa4d1d1cf6251Chris Masone 138d17d185d7abd68584f3756a89a7475a5b6109f34Chris Masone def GetBranchBuildsForBoard(self, board): 139fe5a509f533e7de2531a0d0838146859b543569cChris Masone """Get per-branch, per-board builds since last run of this event. 1405bde7dc7d8b0608feb43491d5ac648d11c890f54Chris Masone 1415bde7dc7d8b0608feb43491d5ac648d11c890f54Chris Masone @param board: the board whose builds we want. 14205b1944ebcb8a62ee0cc38a7f90b4131ededab8fChris Masone @return {branch: [build-name]} 143fe5a509f533e7de2531a0d0838146859b543569cChris Masone 144fe5a509f533e7de2531a0d0838146859b543569cChris Masone Must be implemented by subclasses. 1455bde7dc7d8b0608feb43491d5ac648d11c890f54Chris Masone """ 1465bde7dc7d8b0608feb43491d5ac648d11c890f54Chris Masone raise NotImplementedError() 1475bde7dc7d8b0608feb43491d5ac648d11c890f54Chris Masone 1485bde7dc7d8b0608feb43491d5ac648d11c890f54Chris Masone 1492d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone def ShouldHandle(self): 150e8bebaf10711ca802d40c0b7f412e3910999159eChris Masone """Returns True if this BaseEvent should be Handle()'d, False if not. 151e8bebaf10711ca802d40c0b7f412e3910999159eChris Masone 152855d86f838faccb4ad112a222412e29c047b7d3eChris Masone Must be extended by subclasses. 153e8bebaf10711ca802d40c0b7f412e3910999159eChris Masone """ 154855d86f838faccb4ad112a222412e29c047b7d3eChris Masone return self._always_handle 155e8bebaf10711ca802d40c0b7f412e3910999159eChris Masone 156e8bebaf10711ca802d40c0b7f412e3910999159eChris Masone 157e8bebaf10711ca802d40c0b7f412e3910999159eChris Masone def UpdateCriteria(self): 158e8bebaf10711ca802d40c0b7f412e3910999159eChris Masone """Updates internal state used to decide if this event ShouldHandle() 1592d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 1602d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone Must be implemented by subclasses. 1612d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone """ 1622d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone raise NotImplementedError() 1632d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 1642d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 1659f256d91d378cdc0bbffa607ef3572f006d0b0dcDan Shi def FilterTasks(self): 1669f256d91d378cdc0bbffa607ef3572f006d0b0dcDan Shi """Filter the tasks to only return tasks should run now. 1679f256d91d378cdc0bbffa607ef3572f006d0b0dcDan Shi 1689f256d91d378cdc0bbffa607ef3572f006d0b0dcDan Shi One use case is that Nightly task can run at each hour. The override of 1699f256d91d378cdc0bbffa607ef3572f006d0b0dcDan Shi this function in Nightly class will only return the tasks set to run in 1709f256d91d378cdc0bbffa607ef3572f006d0b0dcDan Shi current hour. 1719f256d91d378cdc0bbffa607ef3572f006d0b0dcDan Shi 1729f256d91d378cdc0bbffa607ef3572f006d0b0dcDan Shi @return: A list of tasks can run now. 1739f256d91d378cdc0bbffa607ef3572f006d0b0dcDan Shi """ 1749f256d91d378cdc0bbffa607ef3572f006d0b0dcDan Shi return list(self.tasks) 1759f256d91d378cdc0bbffa607ef3572f006d0b0dcDan Shi 1769f256d91d378cdc0bbffa607ef3572f006d0b0dcDan Shi 17796f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone def Handle(self, scheduler, branch_builds, board, force=False): 178511a9e3a1cfc2c6dd1263c8f6cb667ab12cd7a6fAlex Miller """Runs all tasks in self._tasks that if scheduled, can be 179511a9e3a1cfc2c6dd1263c8f6cb667ab12cd7a6fAlex Miller successfully run. 1802d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone 1812d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone @param scheduler: an instance of DedupingScheduler, as defined in 1822d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone deduping_scheduler.py 18396f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone @param branch_builds: a dict mapping branch name to the build to 18496f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone install for that branch, e.g. 18505b1944ebcb8a62ee0cc38a7f90b4131ededab8fChris Masone {'R18': ['x86-alex-release/R18-1655.0.0'], 18605b1944ebcb8a62ee0cc38a7f90b4131ededab8fChris Masone 'R19': ['x86-alex-release/R19-2077.0.0'] 18705b1944ebcb8a62ee0cc38a7f90b4131ededab8fChris Masone 'factory': ['x86-alex-factory/R19-2077.0.5']} 18896f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone @param board: the board against which to Run() all of self._tasks. 18996f1663ecc7d8821e804b0256fc68be0756ba66eChris Masone @param force: Tell every Task to always Run(). 1902d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone """ 191a3a38172021c03d5c048681467efd132eece72d1Chris Masone logging.info('Handling %s for %s', self.keyword, board) 1922d61ca263d3e571b19604ea4c9d7b55d83c28405Chris Masone # we need to iterate over an immutable copy of self._tasks 1939f256d91d378cdc0bbffa607ef3572f006d0b0dcDan Shi for task in self.FilterTasks(): 1947ce1b36592894e77c9ef2e05f26657891b47484eAlex Miller if task.AvailableHosts(scheduler, board): 19515d42311147778148e0a80b703e789abc6bbd12dDan Shi if not task.Run(scheduler, branch_builds, board, force, 19615d42311147778148e0a80b703e789abc6bbd12dDan Shi self._mv): 197511a9e3a1cfc2c6dd1263c8f6cb667ab12cd7a6fAlex Miller self._tasks.remove(task) 198d621cf2a52f9c13a0880b431fea3898adc8209a3Alex Miller elif task.ShouldHaveAvailableHosts(): 199cd214e0d2fe515b3cc7faacc44e4e539142670eaChris Masone logging.warning('Skipping %s on %s, due to lack of hosts.', 200cd214e0d2fe515b3cc7faacc44e4e539142670eaChris Masone task, board) 201