blob: 7d7b0f348c35783b2e622bf281cad586869a73a6 [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
Xinan Lin028f9582019-12-11 10:55:33 -08009import re
Xixuan Wu303a5192017-08-29 11:10:42 -070010
11import build_lib
12import constants
13import datastore_client
Xixuan Wua5a29442017-10-11 11:03:02 -070014import global_config
Xixuan Wu303a5192017-08-29 11:10:42 -070015import task
16
17# The suffix for section
18_SECTION_SUFFIX = '_params'
19
20
21class BaseEvent(object):
22 """Basic class for a suite scheduler event."""
23
24 # The default keyword and priority for base event. Will be overwritten by
25 # its subclass.
26 KEYWORD = 'base'
27 PRIORITY = constants.Priorities.DEFAULT
28
29 # The interval hours between consequent rounds of events. Default is 6.
30 LAST_EXEC_INTERVAL = 6
31
32 # The number of days between each event to trigger. Default is 1.
33 DAYS_INTERVAL = 1
34
35 # The max lifetime of suites kicked off by this event.
36 TIMEOUT = 24 # Hours
37
38 def __init__(self, event_settings, last_exec_utc, target_exec_utc):
39 """Initialize a base event.
40
41 Args:
42 event_settings: a config_reader.EventSettings object, indicating
43 the event settings.
44 last_exec_utc: The utc datetime.datetime timestamp of the last
45 execution of the event.
46 target_exec_utc: The utc datetime.datetime timestamp of the next
47 execution of the event.
48 """
49 self._update_with_settings(event_settings)
50 self._datastore_client = None
51 self.task_list = []
52
53 # Processing executing time.
54 self.target_exec_utc = target_exec_utc
55 self._set_last_exec(last_exec_utc)
56 self._set_should_handle()
57
58 logging.info('%s Event created:\ntarget_exec_time (utc): %s\n'
59 'last_exec_time (utc): %s\nshould_handle: %s',
60 self.keyword, self.target_exec_utc, self.last_exec_utc,
61 self.should_handle)
62
63 def set_task_list(self, task_list):
64 """Update task list with given input.
65
66 Args:
67 task_list: a new task list for this event.
68 """
69 self.task_list = list(task_list)
70
71 def filter_tasks(self):
72 """Filter tasks from original task list.
73
74 This could be overwritten by subclass of BaseEvent, like Nightly event, or
75 be directly used by event types like NewBuild.
76 """
77 self.task_list = list(self.task_list)
78
Xixuan Wuc6819012019-05-23 11:34:59 -070079 def get_cros_builds(self, lab_config, build_client):
Xixuan Wu303a5192017-08-29 11:10:42 -070080 """Get CrOS builds to run on for this event.
81
82 Args:
Xixuan Wu6fb16272017-10-19 13:16:00 -070083 lab_config: a config_reader.LabConfig object, to read lab configs.
Xixuan Wuc6819012019-05-23 11:34:59 -070084 build_client: a rest_client.BuildBucketBigqueryClient object, to
85 connect Buildbucket Bigquery.
Xixuan Wu303a5192017-08-29 11:10:42 -070086
87 Returns:
Craig Bergstrom58263d32018-04-26 14:11:35 -060088 A two-tuples of dicts containing cros builds, see return from
Xixuan Wu6ec23e32019-05-23 11:56:02 -070089 |build_lib.get_cros_builds_since_date|.
Xixuan Wu303a5192017-08-29 11:10:42 -070090 """
Xixuan Wu6ec23e32019-05-23 11:56:02 -070091 return build_lib.get_cros_builds_since_date(
Xixuan Wuc6819012019-05-23 11:34:59 -070092 build_client, lab_config.get_cros_board_list(), self.since_date)
Xixuan Wu303a5192017-08-29 11:10:42 -070093
94 def get_launch_control_builds(self, lab_config, android_client):
95 """Get launch control builds to run on for this event.
96
97 Args:
98 lab_config: a config_reader.LabConfig object, to read lab configs.
99 android_client: a rest_client.AndroidBuildRestClient object to call
100 android build API.
101
102 Returns:
103 a dict containing launch control builds for Android boards. See return
104 value of |build_lib.get_launch_control_builds_by_branch_targets|.
105 """
106 return build_lib.get_launch_control_builds_by_branch_targets(
107 android_client, lab_config.get_android_board_list(),
108 self.launch_control_branch_targets)
109
Xinan Lin028f9582019-12-11 10:55:33 -0800110 def get_firmware_builds(self, build_client):
111 """Get the latest firmware builds for all boards.
112
113 Args:
114 build_client: a rest_client.BuildBucketBigqueryClient object, to
115 connect Buildbucket Bigquery.
116
117 Returns:
118 A dict of artifact link for the firmware. The key is
119 ([cros|firmware], board).
120 """
121 firmware_builds = build_client.get_latest_passed_firmware_builds()
122 if not firmware_builds:
123 return None
124 firmware_build_dict = {}
125 ARTIFACT_PATTERN = r'gs://chromeos-image-archive/(?P<firmware_build>.+)'
126 for spec, board, artifact in firmware_builds:
127 match = re.match(ARTIFACT_PATTERN, artifact)
128 if not match:
129 logging.debug('Artifact path of firmware is not valid: %s, %s, %s',
130 spec, board, artifact)
131 continue
132 firmware_build = match.group('firmware_build')
133 logging.debug('latest firmware build of (%s, %s): %s',
134 spec, board, firmware_build)
135 firmware_build_dict[(spec, board)] = firmware_build
136 return firmware_build_dict
137
Craig Bergstrom58263d32018-04-26 14:11:35 -0600138 def process_tasks(self, launch_control_builds, cros_builds_tuple,
Xinan Lin028f9582019-12-11 10:55:33 -0800139 firmware_builds, configs):
Xixuan Wu303a5192017-08-29 11:10:42 -0700140 """Schedule tasks in task_list.
141
142 Args:
143 launch_control_builds: the build dict for Android boards, see
144 return value of |get_launch_control_builds|.
Craig Bergstrom58263d32018-04-26 14:11:35 -0600145 cros_builds_tuple: a two-tuple of build dicts for ChromeOS boards,
146 see return value of |get_cros_builds|.
Xinan Lin028f9582019-12-11 10:55:33 -0800147 firmware_builds: a dict of firmware artifact, see return value of
148 |get_firmware_builds|.
Xixuan Wuf4a4c882019-03-15 14:48:26 -0700149 configs: a config_reader.Configs object, to contain several config
150 readers.
Xixuan Wu303a5192017-08-29 11:10:42 -0700151
152 Returns:
153 A list of finished tasks' names.
154 """
Xixuan Wu5451a662017-10-17 10:57:40 -0700155 scheduled_tasks = []
Xixuan Wu303a5192017-08-29 11:10:42 -0700156 for per_task in self.task_list:
157 try:
Craig Bergstrom58263d32018-04-26 14:11:35 -0600158 if per_task.schedule(launch_control_builds, cros_builds_tuple,
Xinan Lin028f9582019-12-11 10:55:33 -0800159 firmware_builds, configs):
Xixuan Wu5451a662017-10-17 10:57:40 -0700160 scheduled_tasks.append(per_task.name)
161 else:
162 logging.debug('No suites are scheduled in suites queue for '
163 'task %s', per_task.name)
Xixuan Wu303a5192017-08-29 11:10:42 -0700164 except task.SchedulingError:
165 logging.exception('Failed to schedule task: %s', per_task.name)
166 continue
167
Xixuan Wu5451a662017-10-17 10:57:40 -0700168 return scheduled_tasks
Xixuan Wu303a5192017-08-29 11:10:42 -0700169
170 def finish(self):
171 """Execute all actions that once an event is finished."""
172 self.datastore_client.set_last_execute_time(
173 self.keyword, self.target_exec_utc)
174
175 def _update_with_settings(self, event_settings):
176 """Update event with given settings from config file.
177
178 Args:
179 event_settings: a config_reader.EventSettings object, indicating
180 the event settings.
181 """
182 self.always_handle = event_settings.always_handle is not None
183 if self.always_handle:
184 self.always_handle = event_settings.always_handle
185
186 def _set_last_exec(self, last_exec_utc):
187 """Process and set last execute time.
188
189 Set last_exec_utc. If last_exec_utc is too old or None, set it as
190 target_exec_utc.
191
192 Args:
193 last_exec_utc: the utc datetime.datetime timestamp of last execute
194 time. None means no last_exec_utc saved for this event in
195 datastore.
196 """
197 if last_exec_utc is not None:
198 self.last_exec_utc = last_exec_utc
199
200 # If this TimedEvent has expired for a period of time, run its
201 # tasks on next available timepoint.
202 if (self.target_exec_utc - self.last_exec_utc > datetime.timedelta(
203 hours=self.LAST_EXEC_INTERVAL)):
204 self.last_exec_utc = self.target_exec_utc
205 else:
206 self.last_exec_utc = self.target_exec_utc
207
208 def _set_should_handle(self):
209 """Update it's the time for the event to be handled."""
Xixuan Wua5a29442017-10-11 11:03:02 -0700210 if self.always_handle or global_config.GAE_TESTING:
Xixuan Wu303a5192017-08-29 11:10:42 -0700211 self.should_handle = True
212 else:
213 if self.last_exec_utc and self.last_exec_utc == self.target_exec_utc:
214 self.should_handle = False
215 else:
216 self.should_handle = True
217
218 @classmethod
219 def section_name(cls):
220 """Getter for the section name of the event.
221
222 Returns:
223 A string representing section name, refers to a section in config file
224 that contains this event's params.
225 """
226 return cls.KEYWORD + _SECTION_SUFFIX
227
228 @property
229 def datastore_client(self):
230 """Getter for private |self._datastore_client| property."""
231 if self._datastore_client is None:
232 self._datastore_client = (
233 datastore_client.LastExecutionRecordStore())
234
235 return self._datastore_client
236
237 @property
238 def keyword(self):
239 """Getter for private |self.KEYWORD| property."""
240 return self.KEYWORD
241
242 @property
243 def launch_control_branch_targets(self):
244 """Get a dict of branch:targets for launch controls from all tasks."""
245 branches = {}
246 for per_task in self.task_list:
247 for branch in per_task.launch_control_branches:
248 branches.setdefault(branch, []).extend(
249 per_task.launch_control_targets)
250
251 return branches