Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(128)

Side by Side Diff: appengine/findit/util_scripts/remote_queries/swarming_task_data_query.py

Issue 1772173002: [Findit] Adding swarming task metadata query page (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Created 4 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 # Copyright 2016 The Chromium 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 """Pulls historical swarming task metadata from Findit and prints a report."""
6
7 from collections import defaultdict
8 from collections import OrderedDict
9 import datetime
10 import os
11 import sys
12
13 _REMOTE_API_DIR = os.path.join(os.path.dirname(__file__), os.path.pardir)
14 sys.path.insert(1, _REMOTE_API_DIR)
15
16 import remote_api
17
18 from model.wf_swarming_task import WfSwarmingTask
19
20
21 NOT_AVAILABLE = 'N/A'
22
23
24 # TODO(lijeffrey): Refactor helper methods into module sharable with
25 # try_job_data_query.py.
26 def _GetAverageOfNumbersInList(numbers):
27 """Returns a float average of numbers or NOT_AVAILABLE if numbers is empty."""
28 return (float(sum(numbers)) / len(numbers)) if numbers else NOT_AVAILABLE
29
30
31 def _FormatDigits(number):
32 """Formats number into a 2-digit float, or NOT_AVAILABLE."""
33 if isinstance(number, float):
34 return float('%.2f' % number)
35 return NOT_AVAILABLE
36
37
38 def _FormatSecondsAsHMS(seconds):
39 """Formats the number of seconds into hours, minutes, seconds."""
40 if seconds == NOT_AVAILABLE:
41 return NOT_AVAILABLE
42
43 minutes, seconds = divmod(seconds, 60)
44 hours, minutes = divmod(minutes, 60)
45 return '%d:%02d:%02d' % (hours, minutes, seconds)
46
47
48 def _FormatStepName(step_name):
49 # Formats step_name to return only the first word (the step name itself).
50 # Step names are expected to be in either the format 'step_name' or
51 # 'step_name on platform'.
52 return step_name.strip().split(' ')[0]
53
54
55 def _CategorizeSwarmingTaskData(swarming_task_list):
56 """Categorizes swarming_task_list into a dict.
57
58 Args:
59 swarming_task_list: A list of WfSwarmingTask objects.
60
61 Returns:
62 A dict in the format:
63 {
64 priority1: {
65 master_name1': {
66 'builder_name1': {
67 'step_name1': [WfSwarmingTask1, WfSwarmingTask2, ...],
68 ...
69 },
70 ...
71 },
72 ...
73 },
74 ...
75 }
76 """
77 categorized_data = defaultdict(
78 lambda: defaultdict(
79 lambda: defaultdict(
80 lambda: defaultdict(list))))
81
82 for swarming_task in swarming_task_list:
83 if not swarming_task.parameters or not swarming_task.tests_statuses:
84 # Disregard any swarming tasks that were triggered before 'parameters' and
85 # 'tests_statuses' were introduced.
86 continue
87
88 priority = swarming_task.parameters['priority']
89 master_name = swarming_task.master_name
90 builder_name = swarming_task.builder_name
91 step_name = swarming_task.key.id()
92
93 categorized_data[priority][master_name][builder_name][step_name].append(
94 swarming_task)
95
96 return categorized_data
97
98
99 def _GetReportInformation(swarming_task_list, start_date, end_date):
100 """Computes and returns swarming task metadata in a dict.
101
102 Args:
103 swarming_task_list: A list of WfSwarmingTask entities.
104 start_date: The earliest request date to compute data.
105 end_date: The latest request date to compute data.
106
107 Returns:
108 A dict in the following format:
109 {
110 'swarming_tasks_per_day': The average number of swwarming tasks
111 requested over the time period specified,
112 'average_execution_time': The average amount of time spent on each
113 swarming task not including in-queue time.
114 'average_time_in_queue': The average amount of time a swarming task
115 spends in-queue before it is picked up.
116 'longest_execution_time': The length of time of the slowest swarming
117 task in the period requested,
118 'shortest_execution_time': The length of time of the fastest swarming
119 task in the period requested.
120 'tests_times_iterations': The number of tests multiplied by the number
121 of iterations that test was run.
122 'average_number_of_iterations': The average number of iterations each
123 test for this step was run.
124 }
125 """
126 swarming_tasks_per_day = NOT_AVAILABLE
127 average_execution_time = NOT_AVAILABLE
128 average_time_in_queue = NOT_AVAILABLE
129 longest_execution_time = NOT_AVAILABLE
130 shortest_execution_time = NOT_AVAILABLE
131 average_number_of_iterations = NOT_AVAILABLE
132 average_number_of_tests_run = NOT_AVAILABLE
133
134 if swarming_task_list:
135 swarming_tasks_per_day = (
136 len(swarming_task_list) / float((end_date - start_date).days))
137 execution_times_seconds = []
138 in_queue_times = []
139 iteration_counts = []
140 tests_counts = []
141
142 for swarming_task in swarming_task_list:
143 # Execution time.
144 if swarming_task.started_time and swarming_task.completed_time:
145 execution_times_seconds.append(
146 (swarming_task.completed_time - swarming_task.started_time).seconds)
147
148 # In-queue time.
149 if swarming_task.started_time and swarming_task.created_time:
150 in_queue_times.append(
151 (swarming_task.started_time - swarming_task.created_time).seconds)
152
153 # Number of iterations.
154 iterations_to_rerun = swarming_task.parameters.get(
155 'iterations_to_rerun')
156 if iterations_to_rerun is not None:
157 iteration_counts.append(iterations_to_rerun)
158
159 # Number of tests.
160 number_of_tests = len(swarming_task.tests_statuses)
161 if number_of_tests:
162 tests_counts.append(number_of_tests)
163
164 average_execution_time = (_GetAverageOfNumbersInList(
165 execution_times_seconds) if execution_times_seconds else NOT_AVAILABLE)
166 average_time_in_queue = (
167 _GetAverageOfNumbersInList(in_queue_times) if in_queue_times else
168 NOT_AVAILABLE)
169 longest_execution_time = (
170 str(datetime.timedelta(seconds=max(execution_times_seconds)))
171 if execution_times_seconds else NOT_AVAILABLE)
172 shortest_execution_time = (
173 str(datetime.timedelta(seconds=min(execution_times_seconds)))
174 if execution_times_seconds else NOT_AVAILABLE)
175 average_number_of_iterations = _GetAverageOfNumbersInList(iteration_counts)
176 average_number_of_tests_run = _GetAverageOfNumbersInList(tests_counts)
177 tests_times_iterations = (
178 average_number_of_iterations * average_number_of_tests_run)
179
180 return {
181 'swarming_tasks_per_day': swarming_tasks_per_day,
182 'average_execution_time': average_execution_time,
183 'average_time_in_queue': average_time_in_queue,
184 'longest_execution_time': longest_execution_time,
185 'shortest_execution_time': shortest_execution_time,
186 'tests_times_iterations': tests_times_iterations,
187 'average_number_of_iterations': average_number_of_iterations,
188 'average_number_of_tests_run': average_number_of_tests_run,
189 }
190
191
192 def _GetReport(categorized_swarming_task_dict, start_date, end_date):
193 """Returns a swarming task data report as an ordered dict sorted by priority.
194
195 Args:
196 categorized_swarming_task_dict: A dict categorizing WFSwarmingTask entities
197 organized by priority, master_name, builder_name, step_name. This dict
198 should be the output from _CategorizeSwarmingTaskData().
199 start_date: The earliest request date for which data should be computed.
200 end_date: The latest request date for which data should be computed.
201
202 Returns:
203 An ordered dict by highest priority (lower priority number) swarming tasks
204 in the format:
205 {
206 priority: {
207 'master_name': {
208 'builder_name': {
209 'step_name': {
210 'swarming_tasks_per_day': number or 'N/A',
211 'average_execution_time': number or 'N/A',
212 'average_time_in_queue': number or 'N/A',
213 'longest_execution_time': number or 'N/A',
214 'shortest_execution_time': number or 'N/A',
215 'tests_times_iterations': number or 'N/A'
216 'average_number_of_tests_run': number or 'N/A',
217 'average_number_of_iterations': number or 'N/A',
218 },
219 ...
220 },
221 ...
222 },
223 ...
224 },
225 ...
226 }
227 """
228 report = {}
229
230 for priority, masters in categorized_swarming_task_dict.iteritems():
231 report[priority] = {}
232
233 for master, builders in masters.iteritems():
234 report[priority][master] = {}
235
236 for builder, steps in builders.iteritems():
237 report[priority][master][builder] = {}
238
239 for step, swarming_task_data_list in steps.iteritems():
chanli 2016/03/08 23:31:39 nit: Since the layers are fixed, maybe you can use
lijeffrey 2016/03/09 18:36:52 Done.
240 report[priority][master][builder][step] = _GetReportInformation(
241 swarming_task_data_list, start_date, end_date)
242
243 return OrderedDict(sorted(report.items()))
244
245
246 def CreateHtmlPage(report, start_date, end_date):
247 """Generates an html string for displaying the report.
248
249 Args:
250 report: A dict containing all the relevant information returned from
251 _GetReport().
252 start_date: The earliest date that a swarming task was requested.
253 end_date: The latest date that a swarming task was requested.
254
255 Returns:
256 A string containing the html body for the final report page.
257 """
258 html = """
259 <style>
260 table {
261 border-collapse: collapse;
262 border: 1px solid gray;
263 }
264 table td, th {
265 border: 1px solid gray;
266 }
267 </style>"""
268 html += '<b>Swarming task metadata from %s to %s (%s days)</b>' % (
269 str(start_date), str(end_date), (end_date - start_date).days)
270 html += '<h1>Aggregate metadata for swarming tasks by priority</h1>'
271
272 cell_template = '<td>%s</td>'
273
274 for priority, masters in report.iteritems():
275 html += '<h2>Task Priority: %s</h2>' % priority
276 html += """
277 <table>
278 <tr>
279 <th>Master</th>
280 <th>Builder</th>
281 <th>Step</th>
282 <th>Average # Tasks Per Day</th>
283 <th>Average Time In Queue</th>
284 <th>Average Execution Time</th>
285 <th>Longest Execution Time</th>
286 <th>Shortest Execution Time</th>
287 <th># Tests * # Iterations</th>
288 <th>Average # Iterations</th>
289 <th>Average # Tests Run</th>
290 </tr>"""
291
292 for master_name, builder_reports in masters.iteritems():
293 for builder_name, steps in builder_reports.iteritems():
294 for step_name in steps:
295 builder_report = (
296 report[priority][master_name][builder_name][step_name])
297
298 html += '<tr>'
299 html += cell_template % master_name
300 html += cell_template % builder_name
301 html += cell_template % _FormatStepName(step_name)
302 html += cell_template % _FormatDigits(
303 builder_report['swarming_tasks_per_day'])
304 html += cell_template % _FormatSecondsAsHMS(_FormatDigits(
305 builder_report['average_time_in_queue']))
306 html += cell_template % _FormatSecondsAsHMS(_FormatDigits(
307 builder_report['average_execution_time']))
308 html += cell_template % builder_report['longest_execution_time']
309 html += cell_template % builder_report['shortest_execution_time']
310 html += cell_template % _FormatDigits(
311 builder_report['tests_times_iterations'])
312 html += cell_template % _FormatDigits(
313 builder_report['average_number_of_iterations'])
314 html += cell_template % _FormatDigits(
315 builder_report['average_number_of_tests_run'])
316
317 html += '</table>'
318
319 return html
320
321
322 if __name__ == '__main__':
323 # Set up the Remote API to use services on the live App Engine.
324 remote_api.EnableRemoteApi(app_id='findit-for-me')
325
326 START_DATE = datetime.datetime(2016, 2, 1)
327 END_DATE = datetime.datetime(2016, 3, 7)
328
329 wf_analysis_query = WfSwarmingTask.query(
330 WfSwarmingTask.created_time >= START_DATE,
331 WfSwarmingTask.created_time < END_DATE)
332 data_list = wf_analysis_query.fetch()
333
334 categorized_data_dict = _CategorizeSwarmingTaskData(data_list)
335 final_report = _GetReport(categorized_data_dict, START_DATE, END_DATE)
336
337 findit_tmp_dir = os.environ.get('TMP_DIR')
338 if not findit_tmp_dir:
339 findit_tmp_dir = os.getcwd()
340
341 report_path = os.path.join(findit_tmp_dir,
342 'swarming_task_metadata_report.html')
343
344 with open(report_path, 'w') as f:
345 f.write(CreateHtmlPage(final_report, START_DATE, END_DATE))
346
347 print 'Swarming task metadata report available at file://%s' % report_path
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698