blob: 030b7f9e30e312be5df8d6005f056d52ce6abed7 [file] [log] [blame]
Xixuan Wu27a61f82017-09-14 11:42:37 -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 for cron jobs kicked off by suite scheduler."""
6# pylint: disable=g-bad-import-order
7
Xixuan Wu69a5e782017-11-06 14:32:05 -08008import datetime
Xixuan Wu27a61f82017-09-14 11:42:37 -07009import logging
10import webapp2
11
12import constants
Craig Bergstrom3212fb42018-04-17 19:24:15 -060013import config_reader
14import file_getter
Xixuan Wua5a29442017-10-11 11:03:02 -070015import global_config
Xixuan Wu27a61f82017-09-14 11:42:37 -070016import rest_client
Xixuan Wu5d700dc2018-08-21 15:13:10 -070017import swarming_lib
Xixuan Wu27a61f82017-09-14 11:42:37 -070018import task_executor
19import time_converter
20import trigger_receiver
21
22
23_PROD_CALENDAR_ID_MAPPING = {
24 'new_build': constants.CalendarID.PROD_NEW_BUILD,
25 'nightly': constants.CalendarID.PROD_NIGHTLY,
26 'weekly': constants.CalendarID.PROD_WEEKLY,
27}
28
29_STAGING_CALENDAR_ID_MAPPING = {
30 'new_build': constants.CalendarID.STAGING_NEW_BUILD,
31 'nightly': constants.CalendarID.STAGING_NIGHTLY,
32 'weekly': constants.CalendarID.STAGING_WEEKLY,
33}
34
Xixuan Wu69a5e782017-11-06 14:32:05 -080035# Timezone used in calendar, indicating PST.
Xixuan Wu27a61f82017-09-14 11:42:37 -070036_CALENDAR_TIMEZONE = 'America/Los_Angeles'
37
Xixuan Wu69a5e782017-11-06 14:32:05 -080038# Log time extension minutes.
39_LOG_TIME_ADDON_MIN = 1
40
41# The format of url of loggings.
42_LOGGING_URL_FORMAT = 'http://%s/logger?start_time=%s&end_time=%s&resource=%s'
43
Xixuan Wu27a61f82017-09-14 11:42:37 -070044
45class TriggerEvent(webapp2.RequestHandler):
46 """Trigger events regularly to schedule tasks for suite_scheduler."""
47
48 def get(self):
Xixuan Wu9be29082017-10-11 08:49:10 -070049 # Don't kick off cron job in staging instance. This cron job can be
50 # kicked off On local development env or prod instance.
Xixuan Wua5a29442017-10-11 11:03:02 -070051 if global_config.is_in_staging():
Xixuan Wu9be29082017-10-11 08:49:10 -070052 return
53
Xixuan Wu27a61f82017-09-14 11:42:37 -070054 start_time = time_converter.pst_now()
Xixuan Wu69a5e782017-11-06 14:32:05 -080055 utc_start_time = time_converter.utc_now()
Xixuan Wu27a61f82017-09-14 11:42:37 -070056 suite_trigger = trigger_receiver.TriggerReceiver()
57 suite_trigger.cron()
58 end_time = time_converter.pst_now()
Xixuan Wu69a5e782017-11-06 14:32:05 -080059 utc_end_time = time_converter.utc_now()
Xixuan Wu27a61f82017-09-14 11:42:37 -070060 calendar_client = rest_client.CalendarRestClient(
61 rest_client.BaseRestClient(
62 constants.RestClient.CALENDAR_CLIENT.scopes,
63 constants.RestClient.CALENDAR_CLIENT.service_name,
64 constants.RestClient.CALENDAR_CLIENT.service_version))
Xixuan Wu69a5e782017-11-06 14:32:05 -080065 str_start_time, str_end_time = _adjust_log_time(
66 utc_start_time, utc_end_time)
67 log_url = _LOGGING_URL_FORMAT % (
68 constants.server_name(), str_start_time, str_end_time,
69 '/cron/trigger_event')
70 logging.info('URL for logs of current round of event trigger: %s', log_url)
Xixuan Wu27a61f82017-09-14 11:42:37 -070071
72 for keyword, results in suite_trigger.event_results.iteritems():
73 # No finished tasks for the given keyword
74 if not results:
75 continue
76
Xixuan Wu69a5e782017-11-06 14:32:05 -080077 _add_to_calendar(keyword, results, log_url, start_time, end_time,
78 calendar_client, _PROD_CALENDAR_ID_MAPPING[keyword])
Xixuan Wu27a61f82017-09-14 11:42:37 -070079
80
81class ExecuteTask(webapp2.RequestHandler):
82 """Run scheduled tasks regularly for suite_scheduler."""
83
84 def get(self):
Xixuan Wua5a29442017-10-11 11:03:02 -070085 if global_config.is_in_staging():
Xixuan Wu9be29082017-10-11 08:49:10 -070086 return
87
Craig Bergstrom3212fb42018-04-17 19:24:15 -060088 lab_config = config_reader.LabConfig(config_reader.ConfigReader(
89 file_getter.LAB_CONFIG_FILE))
90 afe_server = lab_config.get_dedicated_afe()
Xixuan Wu5d700dc2018-08-21 15:13:10 -070091 task_processor = task_executor.TaskProcessor(
92 task_executor.SUITES_QUEUE,
93 afe_server,
Prathmesh Prabhucf670742018-09-27 15:13:48 -070094 swarming_lib.SKYLAB_SWARMING_SERVER)
Xixuan Wu27a61f82017-09-14 11:42:37 -070095 task_processor.batch_execute()
96
97
Xixuan Wua5a29442017-10-11 11:03:02 -070098class TestPush(webapp2.RequestHandler):
99 """Test push for suite_scheduler on staging instance."""
100
101 def get(self):
Xixuan Wu7f8330f2017-10-13 16:00:52 -0700102 if not global_config.is_in_staging():
Xixuan Wua5a29442017-10-11 11:03:02 -0700103 return
104
105 # Test Cron jobs
106 # 1) No tasks will be filtered by nightly/weekly constraints.
107 # 2) Randomly select builds in |get_cros_builds_since_date_from_db|.
108 # 3) Every task will be kicked off with dummy swarming run |dummy_run|.
Xixuan Wu69a5e782017-11-06 14:32:05 -0800109 start_time = time_converter.pst_now()
110 utc_start_time = time_converter.utc_now()
Xixuan Wua5a29442017-10-11 11:03:02 -0700111 suite_trigger = trigger_receiver.TriggerReceiver()
112 suite_trigger.cron()
Xixuan Wu69a5e782017-11-06 14:32:05 -0800113 end_time = time_converter.pst_now()
114 utc_end_time = time_converter.utc_now()
115 calendar_client = rest_client.CalendarRestClient(
116 rest_client.BaseRestClient(
117 constants.RestClient.CALENDAR_CLIENT.scopes,
118 constants.RestClient.CALENDAR_CLIENT.service_name,
119 constants.RestClient.CALENDAR_CLIENT.service_version))
120 str_start_time, str_end_time = _adjust_log_time(
121 utc_start_time, utc_end_time)
122 log_url = _LOGGING_URL_FORMAT % (
123 constants.server_name(), str_start_time, str_end_time,
124 '/cron/test_push')
125 logging.info('URL for logs of current round of test_push: %s', log_url)
Xixuan Wua5a29442017-10-11 11:03:02 -0700126
Craig Bergstrombdf45212018-05-04 12:07:59 -0600127 lab_config = config_reader.LabConfig(config_reader.ConfigReader(
128 file_getter.LAB_CONFIG_FILE))
129 afe_server = lab_config.get_dedicated_afe()
Xixuan Wu5d700dc2018-08-21 15:13:10 -0700130 task_processor = task_executor.TaskProcessor(
131 task_executor.SUITES_QUEUE,
132 afe_server,
Prathmesh Prabhucf670742018-09-27 15:13:48 -0700133 swarming_lib.SKYLAB_STAGING_SWARMING_SERVER)
Xixuan Wua5a29442017-10-11 11:03:02 -0700134 task_processor.batch_execute()
135 # In testing, after one round of batch_execute() to execute all tasks,
136 # the suite queue will be purged.
137 task_processor.purge()
138
Xixuan Wu69a5e782017-11-06 14:32:05 -0800139 for keyword, results in suite_trigger.event_results.iteritems():
140 # No finished tasks for the given keyword
141 if not results:
142 continue
143
144 _add_to_calendar(keyword, results, log_url, start_time, end_time,
145 calendar_client, _STAGING_CALENDAR_ID_MAPPING[keyword])
146
147
148def _adjust_log_time(utc_start_time, utc_end_time):
149 """Adjust the start and end time for logging.
150
151 In order to ensure that logs can be fetched by a proper time window,
152 slightly enlarge the log time window by extending 2 * _LOG_TIME_ADDON_MIN.
153
154 Args:
155 utc_start_time: a datetime.datetime object in UTC indicating when the logs
156 are started.
157 utc_end_time: a datetime.datetime object in UTC indicating when the logs
158 are ended.
159
160 Returns:
161 A tuple of two strings indicating start_time and end_time in format
162 time_converter.STACKDRIVER_TIME_FORMAT.
163 """
164 real_start_time = utc_start_time - datetime.timedelta(
165 minutes=_LOG_TIME_ADDON_MIN)
166 real_end_time = utc_end_time + datetime.timedelta(
167 minutes=_LOG_TIME_ADDON_MIN)
168 return (
169 real_start_time.strftime(time_converter.STACKDRIVER_TIME_FORMAT),
170 real_end_time.strftime(time_converter.STACKDRIVER_TIME_FORMAT))
171
172
173def _add_to_calendar(keyword, task_results, log_url, start_time, end_time,
174 calendar_client, calendar_id):
175 """Formalize task results and add them to calendar.
176
177 Args:
178 keyword: the suite scheduler event type.
179 task_results: the finished tasks, represented by a list.
180 log_url: a string url for fetching the logs.
181 start_time: a datetime.datetime object in PST.
182 end_time: a datetime.datetime object in PST.
183 calendar_client: a rest_client.CalendarRestClient object.
184 calendar_id: a string calendar ID.
185 """
186 event = {}
187 event['summary'] = '%s Suite Tasks - Scheduled' % keyword
188 description = '<a href=%s>Running Logs</a>\n%d scheduled tasks: \n' % (
189 log_url, len(task_results))
190 for task_name in task_results:
191 description += task_name + '\n'
192 event['description'] = description
193 # The start time is exactly when the cron job is triggered, i.e. when
194 # all tasks of this event are pushed into the task queue. It's not
195 # when the tasks are 'really' kicked off in lab because there will be
196 # some time delay since task queue will schedule tasks batch by batch.
197 # However, the end time is not exactly when the cron job is finished,
198 # since this cron job's runtime is very short, but the calendar won't
199 # show any item that's shorter than 30 minutes. So no matter what
200 # endtime we set here, it will show a half-an-hour event.
201 event['start'] = {
202 'dateTime': start_time.strftime(time_converter.CALENDAR_TIME_FORMAT),
203 'timeZone': _CALENDAR_TIMEZONE,
204 }
205 event['end'] = {
206 'dateTime': end_time.strftime(time_converter.CALENDAR_TIME_FORMAT),
207 'timeZone': _CALENDAR_TIMEZONE,
208 }
209 if constants.environment() == constants.RunningEnv.ENV_PROD:
210 calendar_client.add_event(calendar_id, event)
211 else:
212 logging.info(event)
213
Xixuan Wua5a29442017-10-11 11:03:02 -0700214
Xixuan Wu27a61f82017-09-14 11:42:37 -0700215app = webapp2.WSGIApplication([
216 ('/cron/trigger_event', TriggerEvent),
217 ('/cron/execute_task', ExecuteTask),
Xixuan Wua5a29442017-10-11 11:03:02 -0700218 ('/cron/test_push', TestPush),
Xixuan Wu27a61f82017-09-14 11:42:37 -0700219], debug=True)