blob: 2db2a0def6362737f1cd7b09318ed09628541782 [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 Wuc6819012019-05-23 11:34:59 -070078 def get_cros_builds(self, lab_config, build_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 Wuc6819012019-05-23 11:34:59 -070083 build_client: a rest_client.BuildBucketBigqueryClient object, to
84 connect Buildbucket Bigquery.
Xixuan Wu303a5192017-08-29 11:10:42 -070085
86 Returns:
Craig Bergstrom58263d32018-04-26 14:11:35 -060087 A two-tuples of dicts containing cros builds, see return from
Xixuan Wu6ec23e32019-05-23 11:56:02 -070088 |build_lib.get_cros_builds_since_date|.
Xixuan Wu303a5192017-08-29 11:10:42 -070089 """
Xixuan Wu6ec23e32019-05-23 11:56:02 -070090 return build_lib.get_cros_builds_since_date(
Xixuan Wuc6819012019-05-23 11:34:59 -070091 build_client, lab_config.get_cros_board_list(), self.since_date)
Xixuan Wu303a5192017-08-29 11:10:42 -070092
93 def get_launch_control_builds(self, lab_config, android_client):
94 """Get launch control builds to run on for this event.
95
96 Args:
97 lab_config: a config_reader.LabConfig object, to read lab configs.
98 android_client: a rest_client.AndroidBuildRestClient object to call
99 android build API.
100
101 Returns:
102 a dict containing launch control builds for Android boards. See return
103 value of |build_lib.get_launch_control_builds_by_branch_targets|.
104 """
105 return build_lib.get_launch_control_builds_by_branch_targets(
106 android_client, lab_config.get_android_board_list(),
107 self.launch_control_branch_targets)
108
Craig Bergstrom58263d32018-04-26 14:11:35 -0600109 def process_tasks(self, launch_control_builds, cros_builds_tuple,
Xixuan Wuc6819012019-05-23 11:34:59 -0700110 configs, build_client):
Xixuan Wu303a5192017-08-29 11:10:42 -0700111 """Schedule tasks in task_list.
112
113 Args:
114 launch_control_builds: the build dict for Android boards, see
115 return value of |get_launch_control_builds|.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600116 cros_builds_tuple: a two-tuple of build dicts for ChromeOS boards,
117 see return value of |get_cros_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700118 configs: a config_reader.Configs object, to contain several config
119 readers.
Xixuan Wuc6819012019-05-23 11:34:59 -0700120 build_client: a rest_client.BuildBucketBigqueryClient object, to
121 connect Buildbucket Bigquery.
Xixuan Wu303a5192017-08-29 11:10:42 -0700122
123 Returns:
124 A list of finished tasks' names.
125 """
Xixuan Wu5451a662017-10-17 10:57:40 -0700126 scheduled_tasks = []
Xixuan Wu303a5192017-08-29 11:10:42 -0700127 for per_task in self.task_list:
128 try:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600129 if per_task.schedule(launch_control_builds, cros_builds_tuple,
Xixuan Wuc6819012019-05-23 11:34:59 -0700130 configs, build_client):
Xixuan Wu5451a662017-10-17 10:57:40 -0700131 scheduled_tasks.append(per_task.name)
132 else:
133 logging.debug('No suites are scheduled in suites queue for '
134 'task %s', per_task.name)
Xixuan Wu303a5192017-08-29 11:10:42 -0700135 except task.SchedulingError:
136 logging.exception('Failed to schedule task: %s', per_task.name)
137 continue
138
Xixuan Wu5451a662017-10-17 10:57:40 -0700139 return scheduled_tasks
Xixuan Wu303a5192017-08-29 11:10:42 -0700140
141 def finish(self):
142 """Execute all actions that once an event is finished."""
143 self.datastore_client.set_last_execute_time(
144 self.keyword, self.target_exec_utc)
145
146 def _update_with_settings(self, event_settings):
147 """Update event with given settings from config file.
148
149 Args:
150 event_settings: a config_reader.EventSettings object, indicating
151 the event settings.
152 """
153 self.always_handle = event_settings.always_handle is not None
154 if self.always_handle:
155 self.always_handle = event_settings.always_handle
156
157 def _set_last_exec(self, last_exec_utc):
158 """Process and set last execute time.
159
160 Set last_exec_utc. If last_exec_utc is too old or None, set it as
161 target_exec_utc.
162
163 Args:
164 last_exec_utc: the utc datetime.datetime timestamp of last execute
165 time. None means no last_exec_utc saved for this event in
166 datastore.
167 """
168 if last_exec_utc is not None:
169 self.last_exec_utc = last_exec_utc
170
171 # If this TimedEvent has expired for a period of time, run its
172 # tasks on next available timepoint.
173 if (self.target_exec_utc - self.last_exec_utc > datetime.timedelta(
174 hours=self.LAST_EXEC_INTERVAL)):
175 self.last_exec_utc = self.target_exec_utc
176 else:
177 self.last_exec_utc = self.target_exec_utc
178
179 def _set_should_handle(self):
180 """Update it's the time for the event to be handled."""
Xixuan Wua5a29442017-10-11 11:03:02 -0700181 if self.always_handle or global_config.GAE_TESTING:
Xixuan Wu303a5192017-08-29 11:10:42 -0700182 self.should_handle = True
183 else:
184 if self.last_exec_utc and self.last_exec_utc == self.target_exec_utc:
185 self.should_handle = False
186 else:
187 self.should_handle = True
188
189 @classmethod
190 def section_name(cls):
191 """Getter for the section name of the event.
192
193 Returns:
194 A string representing section name, refers to a section in config file
195 that contains this event's params.
196 """
197 return cls.KEYWORD + _SECTION_SUFFIX
198
199 @property
200 def datastore_client(self):
201 """Getter for private |self._datastore_client| property."""
202 if self._datastore_client is None:
203 self._datastore_client = (
204 datastore_client.LastExecutionRecordStore())
205
206 return self._datastore_client
207
208 @property
209 def keyword(self):
210 """Getter for private |self.KEYWORD| property."""
211 return self.KEYWORD
212
213 @property
214 def launch_control_branch_targets(self):
215 """Get a dict of branch:targets for launch controls from all tasks."""
216 branches = {}
217 for per_task in self.task_list:
218 for branch in per_task.launch_control_branches:
219 branches.setdefault(branch, []).extend(
220 per_task.launch_control_targets)
221
222 return branches