blob: 7c2c97367010374925643a01433856d86e1cd288 [file] [log] [blame]
Xixuan Wu303a5192017-08-29 11:10:42 -07001# Copyright 2017 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"""Module of a basic event."""
6
7import datetime
8import logging
9
10import build_lib
11import constants
12import datastore_client
Xixuan Wua5a29442017-10-11 11:03:02 -070013import global_config
Xixuan Wu303a5192017-08-29 11:10:42 -070014import task
15
16# The suffix for section
17_SECTION_SUFFIX = '_params'
18
19
20class BaseEvent(object):
21 """Basic class for a suite scheduler event."""
22
23 # The default keyword and priority for base event. Will be overwritten by
24 # its subclass.
25 KEYWORD = 'base'
26 PRIORITY = constants.Priorities.DEFAULT
27
28 # The interval hours between consequent rounds of events. Default is 6.
29 LAST_EXEC_INTERVAL = 6
30
31 # The number of days between each event to trigger. Default is 1.
32 DAYS_INTERVAL = 1
33
34 # The max lifetime of suites kicked off by this event.
35 TIMEOUT = 24 # Hours
36
37 def __init__(self, event_settings, last_exec_utc, target_exec_utc):
38 """Initialize a base event.
39
40 Args:
41 event_settings: a config_reader.EventSettings object, indicating
42 the event settings.
43 last_exec_utc: The utc datetime.datetime timestamp of the last
44 execution of the event.
45 target_exec_utc: The utc datetime.datetime timestamp of the next
46 execution of the event.
47 """
48 self._update_with_settings(event_settings)
49 self._datastore_client = None
50 self.task_list = []
51
52 # Processing executing time.
53 self.target_exec_utc = target_exec_utc
54 self._set_last_exec(last_exec_utc)
55 self._set_should_handle()
56
57 logging.info('%s Event created:\ntarget_exec_time (utc): %s\n'
58 'last_exec_time (utc): %s\nshould_handle: %s',
59 self.keyword, self.target_exec_utc, self.last_exec_utc,
60 self.should_handle)
61
62 def set_task_list(self, task_list):
63 """Update task list with given input.
64
65 Args:
66 task_list: a new task list for this event.
67 """
68 self.task_list = list(task_list)
69
70 def filter_tasks(self):
71 """Filter tasks from original task list.
72
73 This could be overwritten by subclass of BaseEvent, like Nightly event, or
74 be directly used by event types like NewBuild.
75 """
76 self.task_list = list(self.task_list)
77
Xixuan Wu6fb16272017-10-19 13:16:00 -070078 def get_cros_builds(self, lab_config, db_client):
Xixuan Wu303a5192017-08-29 11:10:42 -070079 """Get CrOS builds to run on for this event.
80
81 Args:
Xixuan Wu6fb16272017-10-19 13:16:00 -070082 lab_config: a config_reader.LabConfig object, to read lab configs.
Xixuan Wu303a5192017-08-29 11:10:42 -070083 db_client: a cloud_sql_client.CIDBClient object, to call CIDB.
84
85 Returns:
Craig Bergstrom58263d32018-04-26 14:11:35 -060086 A two-tuples of dicts containing cros builds, see return from
Xixuan Wu303a5192017-08-29 11:10:42 -070087 |build_lib.get_cros_builds_since_date_from_db|.
88 """
89 return build_lib.get_cros_builds_since_date_from_db(
Xixuan Wu6fb16272017-10-19 13:16:00 -070090 db_client, lab_config.get_cros_board_list(), self.since_date)
Xixuan Wu303a5192017-08-29 11:10:42 -070091
92 def get_launch_control_builds(self, lab_config, android_client):
93 """Get launch control builds to run on for this event.
94
95 Args:
96 lab_config: a config_reader.LabConfig object, to read lab configs.
97 android_client: a rest_client.AndroidBuildRestClient object to call
98 android build API.
99
100 Returns:
101 a dict containing launch control builds for Android boards. See return
102 value of |build_lib.get_launch_control_builds_by_branch_targets|.
103 """
104 return build_lib.get_launch_control_builds_by_branch_targets(
105 android_client, lab_config.get_android_board_list(),
106 self.launch_control_branch_targets)
107
Craig Bergstrom58263d32018-04-26 14:11:35 -0600108 def process_tasks(self, launch_control_builds, cros_builds_tuple,
Xixuan Wu303a5192017-08-29 11:10:42 -0700109 lab_config, db_client):
110 """Schedule tasks in task_list.
111
112 Args:
113 launch_control_builds: the build dict for Android boards, see
114 return value of |get_launch_control_builds|.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600115 cros_builds_tuple: a two-tuple of build dicts for ChromeOS boards,
116 see return value of |get_cros_builds|.
Xixuan Wu303a5192017-08-29 11:10:42 -0700117 lab_config: a config.LabConfig object, to read lab config file.
118 db_client: a cloud_sql_client.CIDBClient object to connect to cidb.
119
120 Returns:
121 A list of finished tasks' names.
122 """
Xixuan Wu5451a662017-10-17 10:57:40 -0700123 scheduled_tasks = []
Xixuan Wu303a5192017-08-29 11:10:42 -0700124 for per_task in self.task_list:
125 try:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600126 if per_task.schedule(launch_control_builds, cros_builds_tuple,
Xixuan Wu5451a662017-10-17 10:57:40 -0700127 lab_config, db_client):
128 scheduled_tasks.append(per_task.name)
129 else:
130 logging.debug('No suites are scheduled in suites queue for '
131 'task %s', per_task.name)
Xixuan Wu303a5192017-08-29 11:10:42 -0700132 except task.SchedulingError:
133 logging.exception('Failed to schedule task: %s', per_task.name)
134 continue
135
Xixuan Wu5451a662017-10-17 10:57:40 -0700136 return scheduled_tasks
Xixuan Wu303a5192017-08-29 11:10:42 -0700137
138 def finish(self):
139 """Execute all actions that once an event is finished."""
140 self.datastore_client.set_last_execute_time(
141 self.keyword, self.target_exec_utc)
142
143 def _update_with_settings(self, event_settings):
144 """Update event with given settings from config file.
145
146 Args:
147 event_settings: a config_reader.EventSettings object, indicating
148 the event settings.
149 """
150 self.always_handle = event_settings.always_handle is not None
151 if self.always_handle:
152 self.always_handle = event_settings.always_handle
153
154 def _set_last_exec(self, last_exec_utc):
155 """Process and set last execute time.
156
157 Set last_exec_utc. If last_exec_utc is too old or None, set it as
158 target_exec_utc.
159
160 Args:
161 last_exec_utc: the utc datetime.datetime timestamp of last execute
162 time. None means no last_exec_utc saved for this event in
163 datastore.
164 """
165 if last_exec_utc is not None:
166 self.last_exec_utc = last_exec_utc
167
168 # If this TimedEvent has expired for a period of time, run its
169 # tasks on next available timepoint.
170 if (self.target_exec_utc - self.last_exec_utc > datetime.timedelta(
171 hours=self.LAST_EXEC_INTERVAL)):
172 self.last_exec_utc = self.target_exec_utc
173 else:
174 self.last_exec_utc = self.target_exec_utc
175
176 def _set_should_handle(self):
177 """Update it's the time for the event to be handled."""
Xixuan Wua5a29442017-10-11 11:03:02 -0700178 if self.always_handle or global_config.GAE_TESTING:
Xixuan Wu303a5192017-08-29 11:10:42 -0700179 self.should_handle = True
180 else:
181 if self.last_exec_utc and self.last_exec_utc == self.target_exec_utc:
182 self.should_handle = False
183 else:
184 self.should_handle = True
185
186 @classmethod
187 def section_name(cls):
188 """Getter for the section name of the event.
189
190 Returns:
191 A string representing section name, refers to a section in config file
192 that contains this event's params.
193 """
194 return cls.KEYWORD + _SECTION_SUFFIX
195
196 @property
197 def datastore_client(self):
198 """Getter for private |self._datastore_client| property."""
199 if self._datastore_client is None:
200 self._datastore_client = (
201 datastore_client.LastExecutionRecordStore())
202
203 return self._datastore_client
204
205 @property
206 def keyword(self):
207 """Getter for private |self.KEYWORD| property."""
208 return self.KEYWORD
209
210 @property
211 def launch_control_branch_targets(self):
212 """Get a dict of branch:targets for launch controls from all tasks."""
213 branches = {}
214 for per_task in self.task_list:
215 for branch in per_task.launch_control_branches:
216 branches.setdefault(branch, []).extend(
217 per_task.launch_control_targets)
218
219 return branches