blob: 832523aa88d45719eb8e4fb7d8503f4de8123398 [file] [log] [blame]
Chris Masone2d61ca22012-04-02 16:52:46 -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
Dan Shi15d42312015-12-15 15:37:28 -08005import logging
6import time
7from multiprocessing import pool
Chris Masone2d61ca22012-04-02 16:52:46 -07008
Aviv Keshet4e7722b2013-02-14 15:07:46 -08009import base_event, board_enumerator, build_event
10import task, timed_event
Chris Masone2d61ca22012-04-02 16:52:46 -070011
Aviv Keshet4e7722b2013-02-14 15:07:46 -080012import common
Dan Shi098d1e22015-09-02 10:00:24 -070013from autotest_lib.client.common_lib.cros.graphite import autotest_stats
J. Richard Barnette3cbd76b2013-11-27 12:11:25 -080014from autotest_lib.server import utils
Chris Masone2d61ca22012-04-02 16:52:46 -070015
Dan Shi15d42312015-12-15 15:37:28 -080016POOL_SIZE = 32
Dan Shi098d1e22015-09-02 10:00:24 -070017
18_timer = autotest_stats.Timer('suite_scheduler')
19
Chris Masone2d61ca22012-04-02 16:52:46 -070020class Driver(object):
21 """Implements the main loop of the suite_scheduler.
22
Chris Masonebf8775a2012-09-10 10:44:18 -070023 @var EVENT_CLASSES: list of the event classes Driver supports.
Chris Masonefe5a5092012-04-11 18:29:07 -070024 @var _LOOP_INTERVAL_SECONDS: seconds to wait between loop iterations.
Chris Masone2d61ca22012-04-02 16:52:46 -070025
26 @var _scheduler: a DedupingScheduler, used to schedule jobs with the AFE.
Chris Masone3fba86f2012-04-03 10:06:56 -070027 @var _enumerator: a BoardEnumerator, used to list plaforms known to
Chris Masone2d61ca22012-04-02 16:52:46 -070028 the AFE
Chris Masone855d86f2012-05-07 13:48:07 -070029 @var _events: dict of BaseEvents to be handled each time through main loop.
Chris Masone2d61ca22012-04-02 16:52:46 -070030 """
31
Chris Masonebf8775a2012-09-10 10:44:18 -070032 EVENT_CLASSES = [timed_event.Nightly, timed_event.Weekly,
33 build_event.NewBuild]
Chris Masonefe5a5092012-04-11 18:29:07 -070034 _LOOP_INTERVAL_SECONDS = 5 * 60
Chris Masone2d61ca22012-04-02 16:52:46 -070035
36
Dan Shi23245142015-01-22 13:22:28 -080037 def __init__(self, scheduler, enumerator, is_sanity=False):
Chris Masone2d61ca22012-04-02 16:52:46 -070038 """Constructor
39
Chris Masone67f06d62012-04-12 15:16:56 -070040 @param scheduler: an instance of deduping_scheduler.DedupingScheduler.
41 @param enumerator: an instance of board_enumerator.BoardEnumerator.
Dan Shi23245142015-01-22 13:22:28 -080042 @param is_sanity: Set to True if the driver is created for sanity check.
43 Default is set to False.
Chris Masone2d61ca22012-04-02 16:52:46 -070044 """
Chris Masone67f06d62012-04-12 15:16:56 -070045 self._scheduler = scheduler
46 self._enumerator = enumerator
Dan Shi23245142015-01-22 13:22:28 -080047 task.TotMilestoneManager.is_sanity = is_sanity
Chris Masone2d61ca22012-04-02 16:52:46 -070048
Chris Masone2d61ca22012-04-02 16:52:46 -070049
Chris Masone855d86f2012-05-07 13:48:07 -070050 def RereadAndReprocessConfig(self, config, mv):
51 """Re-read config, re-populate self._events and recreate task lists.
52
53 @param config: an instance of ForgivingConfigParser.
54 @param mv: an instance of ManifestVersions.
55 """
56 config.reread()
57 new_events = self._CreateEventsWithTasks(config, mv)
58 for keyword, event in self._events.iteritems():
59 event.Merge(new_events[keyword])
60
61
Chris Masone93f51d42012-04-18 08:46:52 -070062 def SetUpEventsAndTasks(self, config, mv):
Chris Masone67f06d62012-04-12 15:16:56 -070063 """Populate self._events and create task lists from config.
64
Chris Masone96f16632012-04-04 18:36:03 -070065 @param config: an instance of ForgivingConfigParser.
Chris Masone93f51d42012-04-18 08:46:52 -070066 @param mv: an instance of ManifestVersions.
Chris Masone96f16632012-04-04 18:36:03 -070067 """
Chris Masone855d86f2012-05-07 13:48:07 -070068 self._events = self._CreateEventsWithTasks(config, mv)
69
70
71 def _CreateEventsWithTasks(self, config, mv):
72 """Create task lists from config, and assign to newly-minted events.
73
74 Calling multiple times should start afresh each time.
75
76 @param config: an instance of ForgivingConfigParser.
77 @param mv: an instance of ManifestVersions.
78 """
Chris Masone855d86f2012-05-07 13:48:07 -070079 events = {}
Chris Masonebf8775a2012-09-10 10:44:18 -070080 for klass in self.EVENT_CLASSES:
Chris Masone855d86f2012-05-07 13:48:07 -070081 events[klass.KEYWORD] = klass.CreateFromConfig(config, mv)
Chris Masone96f16632012-04-04 18:36:03 -070082
83 tasks = self.TasksFromConfig(config)
Chris Masone645c7e42012-05-17 17:28:40 -070084 for keyword, task_list in tasks.iteritems():
85 if keyword in events:
86 events[keyword].tasks = task_list
87 else:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -070088 logging.warning('%s, is an unknown keyword.', keyword)
Chris Masone855d86f2012-05-07 13:48:07 -070089 return events
Chris Masone96f16632012-04-04 18:36:03 -070090
91
92 def TasksFromConfig(self, config):
93 """Generate a dict of {event_keyword: [tasks]} mappings from |config|.
94
95 For each section in |config| that encodes a Task, instantiate a Task
96 object. Determine the event that Task is supposed to run_on and
97 append the object to a list associated with the appropriate event
98 keyword. Return a dictionary of these keyword: list of task mappings.
99
100 @param config: a ForgivingConfigParser containing tasks to be parsed.
101 @return dict of {event_keyword: [tasks]} mappings.
102 @raise MalformedConfigEntry on a task parsing error.
103 """
104 tasks = {}
105 for section in config.sections():
Chris Masone93f51d42012-04-18 08:46:52 -0700106 if not base_event.HonoredSection(section):
Chris Masone96f16632012-04-04 18:36:03 -0700107 try:
108 keyword, new_task = task.Task.CreateFromConfigSection(
109 config, section)
110 except task.MalformedConfigEntry as e:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700111 logging.warning('%s is malformed: %s', section, e)
Chris Masone96f16632012-04-04 18:36:03 -0700112 continue
113 tasks.setdefault(keyword, []).append(new_task)
114 return tasks
Chris Masone2d61ca22012-04-02 16:52:46 -0700115
116
Chris Masone855d86f2012-05-07 13:48:07 -0700117 def RunForever(self, config, mv):
Chris Masone67f06d62012-04-12 15:16:56 -0700118 """Main loop of the scheduler. Runs til the process is killed.
119
Chris Masone855d86f2012-05-07 13:48:07 -0700120 @param config: an instance of ForgivingConfigParser.
Chris Masone67f06d62012-04-12 15:16:56 -0700121 @param mv: an instance of manifest_versions.ManifestVersions.
122 """
Chris Masone855d86f2012-05-07 13:48:07 -0700123 for event in self._events.itervalues():
Chris Masone73a78382012-04-20 13:25:51 -0700124 event.Prepare()
Chris Masone2d61ca22012-04-02 16:52:46 -0700125 while True:
Chris Masone645c7e42012-05-17 17:28:40 -0700126 try:
127 self.HandleEventsOnce(mv)
Scott Zawalskic15c6b42012-07-09 13:16:05 -0400128 except board_enumerator.EnumeratorException as e:
Ilja H. Friedel04be2bd2014-05-07 21:29:59 -0700129 logging.warning('Failed to enumerate boards: %r', e)
Dan Shi098d1e22015-09-02 10:00:24 -0700130 with _timer.get_client('manifest_versions_update'):
131 mv.Update()
132 with _timer.get_client('tot_milestone_manager_refresh'):
133 task.TotMilestoneManager().refresh()
Chris Masonefe5a5092012-04-11 18:29:07 -0700134 time.sleep(self._LOOP_INTERVAL_SECONDS)
Chris Masone855d86f2012-05-07 13:48:07 -0700135 self.RereadAndReprocessConfig(config, mv)
Chris Masone2d61ca22012-04-02 16:52:46 -0700136
137
Dan Shi15d42312015-12-15 15:37:28 -0800138 @staticmethod
139 def HandleBoard(inputs):
140 """Handle event based on given inputs.
141
142 @param inputs: A dictionary of the arguments needed to handle an event.
143 Keys include:
144 scheduler: a DedupingScheduler, used to schedule jobs with the AFE.
145 event: An event object to be handled.
146 board: Name of the board.
147 """
148 scheduler = inputs['scheduler']
149 event = inputs['event']
150 board = inputs['board']
151
152 logging.info('Handling %s event for board %s', event.keyword, board)
153 branch_builds = event.GetBranchBuildsForBoard(board)
154 event.Handle(scheduler, branch_builds, board)
155 logging.info('Finished handling %s event for board %s', event.keyword,
156 board)
157
158
Dan Shi098d1e22015-09-02 10:00:24 -0700159 @_timer.decorate
Chris Masone67f06d62012-04-12 15:16:56 -0700160 def HandleEventsOnce(self, mv):
161 """One turn through the loop. Separated out for unit testing.
162
163 @param mv: an instance of manifest_versions.ManifestVersions.
Chris Masone645c7e42012-05-17 17:28:40 -0700164 @raise EnumeratorException if we can't enumerate any supported boards.
Chris Masone67f06d62012-04-12 15:16:56 -0700165 """
Chris Masone92874d32012-04-03 10:13:04 -0700166 boards = self._enumerator.Enumerate()
Dan Shi15d42312015-12-15 15:37:28 -0800167 logging.info('%d boards currently in the lab: %r', len(boards), boards)
168 thread_pool = pool.ThreadPool(POOL_SIZE)
Chris Masone855d86f2012-05-07 13:48:07 -0700169 for e in self._events.itervalues():
Chris Masone2d61ca22012-04-02 16:52:46 -0700170 if e.ShouldHandle():
Dan Shi15d42312015-12-15 15:37:28 -0800171 logging.info('Handling %s event for %d boards', e.keyword,
172 len(boards))
173 args = []
Chris Masone96f16632012-04-04 18:36:03 -0700174 for board in boards:
Dan Shi15d42312015-12-15 15:37:28 -0800175 args.append({'scheduler': self._scheduler,
176 'event': e,
177 'board': board})
178 thread_pool.map(self.HandleBoard, args)
179 logging.info('Finished handling %s event for %d boards',
180 e.keyword, len(boards))
Chris Masonebbde3862012-05-07 14:29:51 -0700181 e.UpdateCriteria()
Chris Masone67f06d62012-04-12 15:16:56 -0700182
Dan Shi770a3922015-12-22 10:18:42 -0800183 thread_pool.close()
184
Chris Masone67f06d62012-04-12 15:16:56 -0700185
186 def ForceEventsOnceForBuild(self, keywords, build_name):
187 """Force events with provided keywords to happen, with given build.
188
189 @param keywords: iterable of event keywords to force
190 @param build_name: instead of looking up builds to test, test this one.
191 """
J. Richard Barnette3cbd76b2013-11-27 12:11:25 -0800192 board, type, milestone, manifest = utils.ParseBuildName(build_name)
Chris Masonecbe42772012-04-30 22:18:23 -0700193 branch_builds = {task.PickBranchName(type, milestone): [build_name]}
Alex Millerda26f4f2013-03-07 14:07:44 -0800194 logging.info('Testing build R%s-%s on %s', milestone, manifest, board)
Chris Masone67f06d62012-04-12 15:16:56 -0700195
Chris Masone855d86f2012-05-07 13:48:07 -0700196 for e in self._events.itervalues():
Chris Masone67f06d62012-04-12 15:16:56 -0700197 if e.keyword in keywords:
198 e.Handle(self._scheduler, branch_builds, board, force=True)