blob: 746afc20fae16c7e4a69185eafe504cd1e56ece3 [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
78 def get_cros_builds(self, db_client):
79 """Get CrOS builds to run on for this event.
80
81 Args:
82 db_client: a cloud_sql_client.CIDBClient object, to call CIDB.
83
84 Returns:
85 A dict containing cros builds, see return from
86 |build_lib.get_cros_builds_since_date_from_db|.
87 """
88 return build_lib.get_cros_builds_since_date_from_db(
89 db_client, self.since_date)
90
91 def get_launch_control_builds(self, lab_config, android_client):
92 """Get launch control builds to run on for this event.
93
94 Args:
95 lab_config: a config_reader.LabConfig object, to read lab configs.
96 android_client: a rest_client.AndroidBuildRestClient object to call
97 android build API.
98
99 Returns:
100 a dict containing launch control builds for Android boards. See return
101 value of |build_lib.get_launch_control_builds_by_branch_targets|.
102 """
103 return build_lib.get_launch_control_builds_by_branch_targets(
104 android_client, lab_config.get_android_board_list(),
105 self.launch_control_branch_targets)
106
107 def process_tasks(self, launch_control_builds, cros_builds,
108 lab_config, db_client):
109 """Schedule tasks in task_list.
110
111 Args:
112 launch_control_builds: the build dict for Android boards, see
113 return value of |get_launch_control_builds|.
114 cros_builds: the build dict for ChromeOS boards, see return
115 value of |get_cros_builds|.
116 lab_config: a config.LabConfig object, to read lab config file.
117 db_client: a cloud_sql_client.CIDBClient object to connect to cidb.
118
119 Returns:
120 A list of finished tasks' names.
121 """
122 finished_tasks = []
123 for per_task in self.task_list:
124 try:
125 per_task.schedule(launch_control_builds, cros_builds, lab_config,
126 db_client)
127 finished_tasks.append(per_task.name)
128 except task.SchedulingError:
129 logging.exception('Failed to schedule task: %s', per_task.name)
130 continue
131
132 return finished_tasks
133
134 def finish(self):
135 """Execute all actions that once an event is finished."""
136 self.datastore_client.set_last_execute_time(
137 self.keyword, self.target_exec_utc)
138
139 def _update_with_settings(self, event_settings):
140 """Update event with given settings from config file.
141
142 Args:
143 event_settings: a config_reader.EventSettings object, indicating
144 the event settings.
145 """
146 self.always_handle = event_settings.always_handle is not None
147 if self.always_handle:
148 self.always_handle = event_settings.always_handle
149
150 def _set_last_exec(self, last_exec_utc):
151 """Process and set last execute time.
152
153 Set last_exec_utc. If last_exec_utc is too old or None, set it as
154 target_exec_utc.
155
156 Args:
157 last_exec_utc: the utc datetime.datetime timestamp of last execute
158 time. None means no last_exec_utc saved for this event in
159 datastore.
160 """
161 if last_exec_utc is not None:
162 self.last_exec_utc = last_exec_utc
163
164 # If this TimedEvent has expired for a period of time, run its
165 # tasks on next available timepoint.
166 if (self.target_exec_utc - self.last_exec_utc > datetime.timedelta(
167 hours=self.LAST_EXEC_INTERVAL)):
168 self.last_exec_utc = self.target_exec_utc
169 else:
170 self.last_exec_utc = self.target_exec_utc
171
172 def _set_should_handle(self):
173 """Update it's the time for the event to be handled."""
Xixuan Wua5a29442017-10-11 11:03:02 -0700174 if self.always_handle or global_config.GAE_TESTING:
Xixuan Wu303a5192017-08-29 11:10:42 -0700175 self.should_handle = True
176 else:
177 if self.last_exec_utc and self.last_exec_utc == self.target_exec_utc:
178 self.should_handle = False
179 else:
180 self.should_handle = True
181
182 @classmethod
183 def section_name(cls):
184 """Getter for the section name of the event.
185
186 Returns:
187 A string representing section name, refers to a section in config file
188 that contains this event's params.
189 """
190 return cls.KEYWORD + _SECTION_SUFFIX
191
192 @property
193 def datastore_client(self):
194 """Getter for private |self._datastore_client| property."""
195 if self._datastore_client is None:
196 self._datastore_client = (
197 datastore_client.LastExecutionRecordStore())
198
199 return self._datastore_client
200
201 @property
202 def keyword(self):
203 """Getter for private |self.KEYWORD| property."""
204 return self.KEYWORD
205
206 @property
207 def launch_control_branch_targets(self):
208 """Get a dict of branch:targets for launch controls from all tasks."""
209 branches = {}
210 for per_task in self.task_list:
211 for branch in per_task.launch_control_branches:
212 branches.setdefault(branch, []).extend(
213 per_task.launch_control_targets)
214
215 return branches