blob: 970d528778624d13690a6b6b6568612e3a57c879 [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 """
Xixuan Wu5451a662017-10-17 10:57:40 -0700122 scheduled_tasks = []
Xixuan Wu303a5192017-08-29 11:10:42 -0700123 for per_task in self.task_list:
124 try:
Xixuan Wu5451a662017-10-17 10:57:40 -0700125 if per_task.schedule(launch_control_builds, cros_builds,
126 lab_config, db_client):
127 scheduled_tasks.append(per_task.name)
128 else:
129 logging.debug('No suites are scheduled in suites queue for '
130 'task %s', per_task.name)
Xixuan Wu303a5192017-08-29 11:10:42 -0700131 except task.SchedulingError:
132 logging.exception('Failed to schedule task: %s', per_task.name)
133 continue
134
Xixuan Wu5451a662017-10-17 10:57:40 -0700135 return scheduled_tasks
Xixuan Wu303a5192017-08-29 11:10:42 -0700136
137 def finish(self):
138 """Execute all actions that once an event is finished."""
139 self.datastore_client.set_last_execute_time(
140 self.keyword, self.target_exec_utc)
141
142 def _update_with_settings(self, event_settings):
143 """Update event with given settings from config file.
144
145 Args:
146 event_settings: a config_reader.EventSettings object, indicating
147 the event settings.
148 """
149 self.always_handle = event_settings.always_handle is not None
150 if self.always_handle:
151 self.always_handle = event_settings.always_handle
152
153 def _set_last_exec(self, last_exec_utc):
154 """Process and set last execute time.
155
156 Set last_exec_utc. If last_exec_utc is too old or None, set it as
157 target_exec_utc.
158
159 Args:
160 last_exec_utc: the utc datetime.datetime timestamp of last execute
161 time. None means no last_exec_utc saved for this event in
162 datastore.
163 """
164 if last_exec_utc is not None:
165 self.last_exec_utc = last_exec_utc
166
167 # If this TimedEvent has expired for a period of time, run its
168 # tasks on next available timepoint.
169 if (self.target_exec_utc - self.last_exec_utc > datetime.timedelta(
170 hours=self.LAST_EXEC_INTERVAL)):
171 self.last_exec_utc = self.target_exec_utc
172 else:
173 self.last_exec_utc = self.target_exec_utc
174
175 def _set_should_handle(self):
176 """Update it's the time for the event to be handled."""
Xixuan Wua5a29442017-10-11 11:03:02 -0700177 if self.always_handle or global_config.GAE_TESTING:
Xixuan Wu303a5192017-08-29 11:10:42 -0700178 self.should_handle = True
179 else:
180 if self.last_exec_utc and self.last_exec_utc == self.target_exec_utc:
181 self.should_handle = False
182 else:
183 self.should_handle = True
184
185 @classmethod
186 def section_name(cls):
187 """Getter for the section name of the event.
188
189 Returns:
190 A string representing section name, refers to a section in config file
191 that contains this event's params.
192 """
193 return cls.KEYWORD + _SECTION_SUFFIX
194
195 @property
196 def datastore_client(self):
197 """Getter for private |self._datastore_client| property."""
198 if self._datastore_client is None:
199 self._datastore_client = (
200 datastore_client.LastExecutionRecordStore())
201
202 return self._datastore_client
203
204 @property
205 def keyword(self):
206 """Getter for private |self.KEYWORD| property."""
207 return self.KEYWORD
208
209 @property
210 def launch_control_branch_targets(self):
211 """Get a dict of branch:targets for launch controls from all tasks."""
212 branches = {}
213 for per_task in self.task_list:
214 for branch in per_task.launch_control_branches:
215 branches.setdefault(branch, []).extend(
216 per_task.launch_control_targets)
217
218 return branches