blob: 6d6a8a1350e72c4ac2f65815c60a9c41515a8524 [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
Chris Masone96f16632012-04-04 18:36:03 -07005import logging, time
Chris Masone2d61ca22012-04-02 16:52:46 -07006
Chris Masone93f51d42012-04-18 08:46:52 -07007import base_event, board_enumerator, build_event, deduping_scheduler
8import forgiving_config_parser, manifest_versions, task, timed_event
Chris Masone2d61ca22012-04-02 16:52:46 -07009
10
11class Driver(object):
12 """Implements the main loop of the suite_scheduler.
13
Chris Masonefe5a5092012-04-11 18:29:07 -070014 @var _LOOP_INTERVAL_SECONDS: seconds to wait between loop iterations.
Chris Masone2d61ca22012-04-02 16:52:46 -070015
16 @var _scheduler: a DedupingScheduler, used to schedule jobs with the AFE.
Chris Masone3fba86f2012-04-03 10:06:56 -070017 @var _enumerator: a BoardEnumerator, used to list plaforms known to
Chris Masone2d61ca22012-04-02 16:52:46 -070018 the AFE
Chris Masone855d86f2012-05-07 13:48:07 -070019 @var _events: dict of BaseEvents to be handled each time through main loop.
Chris Masone2d61ca22012-04-02 16:52:46 -070020 """
21
Chris Masonefe5a5092012-04-11 18:29:07 -070022 _LOOP_INTERVAL_SECONDS = 5 * 60
Chris Masone2d61ca22012-04-02 16:52:46 -070023
24
Chris Masone67f06d62012-04-12 15:16:56 -070025 def __init__(self, scheduler, enumerator):
Chris Masone2d61ca22012-04-02 16:52:46 -070026 """Constructor
27
Chris Masone67f06d62012-04-12 15:16:56 -070028 @param scheduler: an instance of deduping_scheduler.DedupingScheduler.
29 @param enumerator: an instance of board_enumerator.BoardEnumerator.
Chris Masone2d61ca22012-04-02 16:52:46 -070030 """
Chris Masone67f06d62012-04-12 15:16:56 -070031 self._scheduler = scheduler
32 self._enumerator = enumerator
Chris Masone2d61ca22012-04-02 16:52:46 -070033
Chris Masone2d61ca22012-04-02 16:52:46 -070034
Chris Masone855d86f2012-05-07 13:48:07 -070035 def RereadAndReprocessConfig(self, config, mv):
36 """Re-read config, re-populate self._events and recreate task lists.
37
38 @param config: an instance of ForgivingConfigParser.
39 @param mv: an instance of ManifestVersions.
40 """
41 config.reread()
42 new_events = self._CreateEventsWithTasks(config, mv)
43 for keyword, event in self._events.iteritems():
44 event.Merge(new_events[keyword])
45
46
Chris Masone93f51d42012-04-18 08:46:52 -070047 def SetUpEventsAndTasks(self, config, mv):
Chris Masone67f06d62012-04-12 15:16:56 -070048 """Populate self._events and create task lists from config.
49
Chris Masone96f16632012-04-04 18:36:03 -070050 @param config: an instance of ForgivingConfigParser.
Chris Masone93f51d42012-04-18 08:46:52 -070051 @param mv: an instance of ManifestVersions.
Chris Masone96f16632012-04-04 18:36:03 -070052 """
Chris Masone855d86f2012-05-07 13:48:07 -070053 self._events = self._CreateEventsWithTasks(config, mv)
54
55
56 def _CreateEventsWithTasks(self, config, mv):
57 """Create task lists from config, and assign to newly-minted events.
58
59 Calling multiple times should start afresh each time.
60
61 @param config: an instance of ForgivingConfigParser.
62 @param mv: an instance of ManifestVersions.
63 """
64 event_classes = [timed_event.Nightly, timed_event.Weekly,
65 build_event.NewBuild]
66 events = {}
67 for klass in event_classes:
68 events[klass.KEYWORD] = klass.CreateFromConfig(config, mv)
Chris Masone96f16632012-04-04 18:36:03 -070069
70 tasks = self.TasksFromConfig(config)
71
Chris Masone855d86f2012-05-07 13:48:07 -070072 for keyword, event in events.iteritems():
73 if keyword in tasks:
74 event.tasks = tasks[keyword]
Chris Masone96f16632012-04-04 18:36:03 -070075 # TODO(cmasone): warn about unknown keywords?
Chris Masone855d86f2012-05-07 13:48:07 -070076 return events
Chris Masone96f16632012-04-04 18:36:03 -070077
78
79 def TasksFromConfig(self, config):
80 """Generate a dict of {event_keyword: [tasks]} mappings from |config|.
81
82 For each section in |config| that encodes a Task, instantiate a Task
83 object. Determine the event that Task is supposed to run_on and
84 append the object to a list associated with the appropriate event
85 keyword. Return a dictionary of these keyword: list of task mappings.
86
87 @param config: a ForgivingConfigParser containing tasks to be parsed.
88 @return dict of {event_keyword: [tasks]} mappings.
89 @raise MalformedConfigEntry on a task parsing error.
90 """
91 tasks = {}
92 for section in config.sections():
Chris Masone93f51d42012-04-18 08:46:52 -070093 if not base_event.HonoredSection(section):
Chris Masone96f16632012-04-04 18:36:03 -070094 try:
95 keyword, new_task = task.Task.CreateFromConfigSection(
96 config, section)
97 except task.MalformedConfigEntry as e:
98 logging.warn('%s is malformed: %s', section, e)
99 continue
100 tasks.setdefault(keyword, []).append(new_task)
101 return tasks
Chris Masone2d61ca22012-04-02 16:52:46 -0700102
103
Chris Masone855d86f2012-05-07 13:48:07 -0700104 def RunForever(self, config, mv):
Chris Masone67f06d62012-04-12 15:16:56 -0700105 """Main loop of the scheduler. Runs til the process is killed.
106
Chris Masone855d86f2012-05-07 13:48:07 -0700107 @param config: an instance of ForgivingConfigParser.
Chris Masone67f06d62012-04-12 15:16:56 -0700108 @param mv: an instance of manifest_versions.ManifestVersions.
109 """
Chris Masone855d86f2012-05-07 13:48:07 -0700110 for event in self._events.itervalues():
Chris Masone73a78382012-04-20 13:25:51 -0700111 event.Prepare()
Chris Masone2d61ca22012-04-02 16:52:46 -0700112 while True:
Chris Masone67f06d62012-04-12 15:16:56 -0700113 self.HandleEventsOnce(mv)
114 mv.Update()
115 # TODO(cmasone): Do we want to run every _LOOP_INTERVAL_SECONDS?
116 # Or is it OK to wait that long between every run?
Chris Masonefe5a5092012-04-11 18:29:07 -0700117 time.sleep(self._LOOP_INTERVAL_SECONDS)
Chris Masone855d86f2012-05-07 13:48:07 -0700118 self.RereadAndReprocessConfig(config, mv)
Chris Masone2d61ca22012-04-02 16:52:46 -0700119
120
Chris Masone67f06d62012-04-12 15:16:56 -0700121 def HandleEventsOnce(self, mv):
122 """One turn through the loop. Separated out for unit testing.
123
124 @param mv: an instance of manifest_versions.ManifestVersions.
125 """
Chris Masone92874d32012-04-03 10:13:04 -0700126 boards = self._enumerator.Enumerate()
Chris Masonea3a38172012-05-14 15:19:56 -0700127 logging.info('Boards currently in the lab: %r', boards)
Chris Masone855d86f2012-05-07 13:48:07 -0700128 for e in self._events.itervalues():
Chris Masone2d61ca22012-04-02 16:52:46 -0700129 if e.ShouldHandle():
Chris Masonea3a38172012-05-14 15:19:56 -0700130 logging.info('Handling %s event', e.keyword)
Chris Masone96f16632012-04-04 18:36:03 -0700131 for board in boards:
Chris Masoned17d1852012-04-20 11:52:49 -0700132 branch_builds = e.GetBranchBuildsForBoard(board)
Chris Masone96f16632012-04-04 18:36:03 -0700133 e.Handle(self._scheduler, branch_builds, board)
Chris Masonebbde3862012-05-07 14:29:51 -0700134 e.UpdateCriteria()
Chris Masone67f06d62012-04-12 15:16:56 -0700135
136
137 def ForceEventsOnceForBuild(self, keywords, build_name):
138 """Force events with provided keywords to happen, with given build.
139
140 @param keywords: iterable of event keywords to force
141 @param build_name: instead of looking up builds to test, test this one.
142 """
143 board, type, milestone, manifest = base_event.ParseBuildName(build_name)
Chris Masonecbe42772012-04-30 22:18:23 -0700144 branch_builds = {task.PickBranchName(type, milestone): [build_name]}
Chris Masone67f06d62012-04-12 15:16:56 -0700145 logging.info('Testing build %s-%s on %s' % (milestone, manifest, board))
146
Chris Masone855d86f2012-05-07 13:48:07 -0700147 for e in self._events.itervalues():
Chris Masone67f06d62012-04-12 15:16:56 -0700148 if e.keyword in keywords:
149 e.Handle(self._scheduler, branch_builds, board, force=True)