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