blob: 47c71724ad91d93e7fd4478ca0c977bcfd26e9e2 [file] [log] [blame]
Caroline Tice48462062016-11-18 16:49:00 -08001#!/usr/bin/env python2
2"""Generate summary report for ChromeOS toolchain waterfalls."""
3
4# Desired future features (to be added):
5# - arguments to allow generating only the main waterfall report,
6# or only the rotating builder reports, or only the failures
7# report; or the waterfall reports without the failures report.
8# - Better way of figuring out which dates/builds to generate
9# reports for: probably an argument specifying a date or a date
10# range, then use something like the new buildbot utils to
11# query the build logs to find the right build numbers for the
12# builders for the specified dates.
13# - Store/get the json/data files in mobiletc-prebuild's x20 area.
14# - Update data in json file to reflect, for each testsuite, which
15# tests are not expected to run on which boards; update this
16# script to use that data appropriately.
17# - Make sure user's prodaccess is up-to-date before trying to use
18# this script.
19# - Add some nice formatting/highlighting to reports.
20
21from __future__ import print_function
22
Caroline Ticee02e9f82016-12-01 13:14:41 -080023import getpass
Caroline Tice48462062016-11-18 16:49:00 -080024import json
25import os
Caroline Ticee02e9f82016-12-01 13:14:41 -080026import shutil
Caroline Tice48462062016-11-18 16:49:00 -080027import sys
28import time
29
30from cros_utils import command_executer
31
32# All the test suites whose data we might want for the reports.
33TESTS = (
34 ('bvt-inline', 'HWTest'),
35 ('bvt-cq', 'HWTest'),
36 ('toolchain-tests', 'HWTest'),
37 ('security', 'HWTest'),
38 ('kernel_daily_regression', 'HWTest'),
39 ('kernel_daily_benchmarks', 'HWTest'),)
40
41# The main waterfall builders, IN THE ORDER IN WHICH WE WANT THEM
42# LISTED IN THE REPORT.
43WATERFALL_BUILDERS = [
44 'amd64-gcc-toolchain', 'arm-gcc-toolchain', 'arm64-gcc-toolchain',
45 'x86-gcc-toolchain', 'amd64-llvm-toolchain', 'arm-llvm-toolchain',
46 'arm64-llvm-toolchain', 'x86-llvm-toolchain', 'amd64-llvm-next-toolchain',
47 'arm-llvm-next-toolchain', 'arm64-llvm-next-toolchain',
48 'x86-llvm-next-toolchain'
49]
50
Caroline Tice48462062016-11-18 16:49:00 -080051DATA_DIR = '/google/data/rw/users/mo/mobiletc-prebuild/waterfall-report-data/'
Caroline Ticee02e9f82016-12-01 13:14:41 -080052ARCHIVE_DIR = '/google/data/rw/users/mo/mobiletc-prebuild/waterfall-reports/'
Caroline Tice48462062016-11-18 16:49:00 -080053DOWNLOAD_DIR = '/tmp/waterfall-logs'
Caroline Ticee02e9f82016-12-01 13:14:41 -080054MAX_SAVE_RECORDS = 7
Caroline Tice48462062016-11-18 16:49:00 -080055BUILD_DATA_FILE = '%s/build-data.txt' % DATA_DIR
56ROTATING_BUILDERS = ['gcc_toolchain', 'llvm_toolchain']
57
58# For int-to-string date conversion. Note, the index of the month in this
59# list needs to correspond to the month's integer value. i.e. 'Sep' must
60# be as MONTHS[9].
61MONTHS = [
62 '', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct',
63 'Nov', 'Dec'
64]
65
66
67def format_date(int_date):
68 """Convert an integer date to a string date. YYYYMMDD -> YYYY-MMM-DD"""
69
70 if int_date == 0:
71 return 'today'
72
73 tmp_date = int_date
74 day = tmp_date % 100
75 tmp_date = tmp_date / 100
76 month = tmp_date % 100
77 year = tmp_date / 100
78
79 month_str = MONTHS[month]
80 date_str = '%d-%s-%d' % (year, month_str, day)
81 return date_str
82
83
Caroline Ticee02e9f82016-12-01 13:14:41 -080084def EmailReport(report_file, report_type, date):
85 subject = '%s Waterfall Summary report, %s' % (report_type, date)
86 email_to = getpass.getuser()
Rahul Chaudhry213f3c02016-12-06 10:47:05 -080087 sendgmr_path = '/google/data/ro/projects/gws-sre/sendgmr'
88 command = ('%s --to=%s@google.com --subject="%s" --body_file=%s' %
89 (sendgmr_path, email_to, subject, report_file))
Caroline Ticee02e9f82016-12-01 13:14:41 -080090 command_executer.GetCommandExecuter().RunCommand(command)
91
92
93def PruneOldFailures(failure_dict, int_date):
94 earliest_date = int_date - MAX_SAVE_RECORDS
95 for suite in failure_dict:
96 suite_dict = failure_dict[suite]
97 test_keys_to_remove = []
98 for test in suite_dict:
99 test_dict = suite_dict[test]
100 msg_keys_to_remove = []
101 for msg in test_dict:
102 fails = test_dict[msg]
103 i = 0
104 while i < len(fails) and fails[i][0] <= earliest_date:
105 i += 1
106 new_fails = fails[i:]
107 test_dict[msg] = new_fails
108 if len(new_fails) == 0:
109 msg_keys_to_remove.append(msg)
110
111 for k in msg_keys_to_remove:
112 del test_dict[k]
113
114 suite_dict[test] = test_dict
115 if len(test_dict) == 0:
116 test_keys_to_remove.append(test)
117
118 for k in test_keys_to_remove:
119 del suite_dict[k]
120
121 failure_dict[suite] = suite_dict
122
123
Caroline Tice48462062016-11-18 16:49:00 -0800124def GenerateWaterfallReport(report_dict, fail_dict, waterfall_type, date):
125 """Write out the actual formatted report."""
126
127 filename = 'waterfall_report.%s_waterfall.%s.txt' % (waterfall_type, date)
128
129 date_string = ''
130 date_list = report_dict['date']
131 num_dates = len(date_list)
132 i = 0
133 for d in date_list:
134 date_string += d
135 if i < num_dates - 1:
136 date_string += ', '
137 i += 1
138
139 if waterfall_type == 'main':
140 report_list = WATERFALL_BUILDERS
141 else:
142 report_list = report_dict.keys()
143
144 with open(filename, 'w') as out_file:
145 # Write Report Header
146 out_file.write('\nStatus of %s Waterfall Builds from %s\n\n' %
147 (waterfall_type, date_string))
148 out_file.write(' '
149 ' kernel kernel\n')
150 out_file.write(' Build bvt- bvt-cq '
151 'toolchain- security daily daily\n')
152 out_file.write(' status inline '
153 ' tests regression benchmarks\n')
154 out_file.write(' [P/ F/ DR]* [P/ F /DR]* '
155 '[P/ F/ DR]* [P/ F/ DR]* [P/ F/ DR]* [P/ F/ DR]*\n\n')
156
157 # Write daily waterfall status section.
158 for i in range(0, len(report_list)):
159 builder = report_list[i]
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800160 if builder == 'date':
161 continue
Caroline Tice48462062016-11-18 16:49:00 -0800162
163 if builder not in report_dict:
164 out_file.write('Unable to find information for %s.\n\n' % builder)
165 continue
166
167 build_dict = report_dict[builder]
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800168 status = build_dict.get('build_status', 'bad')
169 inline = build_dict.get('bvt-inline', '[??/ ?? /??]')
170 cq = build_dict.get('bvt-cq', '[??/ ?? /??]')
171 inline_color = build_dict.get('bvt-inline-color', '')
172 cq_color = build_dict.get('bvt-cq-color', '')
Caroline Tice48462062016-11-18 16:49:00 -0800173 if 'x86' not in builder:
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800174 toolchain = build_dict.get('toolchain-tests', '[??/ ?? /??]')
175 security = build_dict.get('security', '[??/ ?? /??]')
176 toolchain_color = build_dict.get('toolchain-tests-color', '')
177 security_color = build_dict.get('security-color', '')
Caroline Tice48462062016-11-18 16:49:00 -0800178 if 'gcc' in builder:
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800179 regression = build_dict.get('kernel_daily_regression', '[??/ ?? /??]')
180 bench = build_dict.get('kernel_daily_benchmarks', '[??/ ?? /??]')
181 regression_color = build_dict.get('kernel_daily_regression-color', '')
182 bench_color = build_dict.get('kernel_daily_benchmarks-color', '')
183 out_file.write(' %6s %6s'
184 ' %6s %6s %6s %6s\n' %
185 (inline_color, cq_color, toolchain_color,
186 security_color, regression_color, bench_color))
Caroline Tice48462062016-11-18 16:49:00 -0800187 out_file.write('%25s %3s %s %s %s %s %s %s\n' % (builder, status,
188 inline, cq,
189 toolchain, security,
190 regression, bench))
191 else:
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800192 out_file.write(' %6s %6s'
193 ' %6s %6s\n' % (inline_color, cq_color,
194 toolchain_color,
195 security_color))
Caroline Tice48462062016-11-18 16:49:00 -0800196 out_file.write('%25s %3s %s %s %s %s\n' % (builder, status, inline,
197 cq, toolchain, security))
198 else:
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800199 out_file.write(' %6s %6s\n' %
200 (inline_color, cq_color))
Caroline Tice48462062016-11-18 16:49:00 -0800201 out_file.write('%25s %3s %s %s\n' % (builder, status, inline, cq))
202 if 'build_link' in build_dict:
203 out_file.write('%s\n\n' % build_dict['build_link'])
204
205 out_file.write('\n\n*P = Number of tests in suite that Passed; F = '
206 'Number of tests in suite that Failed; DR = Number of tests'
207 ' in suite that Didn\'t Run.\n')
208
209 # Write failure report section.
210 out_file.write('\n\nSummary of Test Failures as of %s\n\n' % date_string)
211
212 # We want to sort the errors and output them in order of the ones that occur
213 # most often. So we have to collect the data about all of them, then sort
214 # it.
215 error_groups = []
216 for suite in fail_dict:
217 suite_dict = fail_dict[suite]
218 if suite_dict:
219 for test in suite_dict:
220 test_dict = suite_dict[test]
221 for err_msg in test_dict:
222 err_list = test_dict[err_msg]
223 sorted_list = sorted(err_list, key=lambda x: x[0], reverse=True)
224 err_group = [len(sorted_list), suite, test, err_msg, sorted_list]
225 error_groups.append(err_group)
226
227 # Sort the errors by the number of errors of each type. Then output them in
228 # order.
229 sorted_errors = sorted(error_groups, key=lambda x: x[0], reverse=True)
230 for i in range(0, len(sorted_errors)):
231 err_group = sorted_errors[i]
232 suite = err_group[1]
233 test = err_group[2]
234 err_msg = err_group[3]
235 err_list = err_group[4]
236 out_file.write('Suite: %s\n' % suite)
237 out_file.write(' %s (%d failures)\n' % (test, len(err_list)))
238 out_file.write(' (%s)\n' % err_msg)
239 for i in range(0, len(err_list)):
240 err = err_list[i]
241 out_file.write(' %s, %s, %s\n' % (format_date(err[0]), err[1],
242 err[2]))
243 out_file.write('\n')
244
245 print('Report generated in %s.' % filename)
Caroline Ticee02e9f82016-12-01 13:14:41 -0800246 return filename
Caroline Tice48462062016-11-18 16:49:00 -0800247
248
249def UpdateReport(report_dict, builder, test, report_date, build_link,
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800250 test_summary, board, color):
Caroline Tice48462062016-11-18 16:49:00 -0800251 """Update the data in our report dictionary with current test's data."""
252
253 if 'date' not in report_dict:
254 report_dict['date'] = [report_date]
255 elif report_date not in report_dict['date']:
256 # It is possible that some of the builders started/finished on different
257 # days, so we allow for multiple dates in the reports.
258 report_dict['date'].append(report_date)
259
260 build_key = ''
261 if builder == 'gcc_toolchain':
262 build_key = '%s-gcc-toolchain' % board
263 elif builder == 'llvm_toolchain':
264 build_key = '%s-llvm-toolchain' % board
265 else:
266 build_key = builder
267
268 if build_key not in report_dict.keys():
269 build_dict = dict()
270 else:
271 build_dict = report_dict[build_key]
272
273 if 'build_link' not in build_dict:
274 build_dict['build_link'] = build_link
275
276 if 'date' not in build_dict:
277 build_dict['date'] = report_date
278
279 if 'board' in build_dict and build_dict['board'] != board:
280 raise RuntimeError('Error: Two different boards (%s,%s) in one build (%s)!'
281 % (board, build_dict['board'], build_link))
282 build_dict['board'] = board
283
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800284 color_key = '%s-color' % test
285 build_dict[color_key] = color
286
Caroline Tice48462062016-11-18 16:49:00 -0800287 # Check to see if we already have a build status for this build_key
288 status = ''
289 if 'build_status' in build_dict.keys():
290 # Use current build_status, unless current test failed (see below).
291 status = build_dict['build_status']
292
293 if not test_summary:
294 # Current test data was not available, so something was bad with build.
295 build_dict['build_status'] = 'bad'
296 build_dict[test] = '[ no data ]'
297 else:
298 build_dict[test] = test_summary
299 if not status:
300 # Current test ok; no other data, so assume build was ok.
301 build_dict['build_status'] = 'ok'
302
303 report_dict[build_key] = build_dict
304
305
306def UpdateBuilds(builds):
307 """Update the data in our build-data.txt file."""
308
309 # The build data file records the last build number for which we
310 # generated a report. When we generate the next report, we read
311 # this data and increment it to get the new data; when we finish
312 # generating the reports, we write the updated values into this file.
313 # NOTE: One side effect of doing this at the end: If the script
314 # fails in the middle of generating a report, this data does not get
315 # updated.
316 with open(BUILD_DATA_FILE, 'w') as fp:
317 gcc_max = 0
318 llvm_max = 0
319 for b in builds:
320 if b[0] == 'gcc_toolchain':
321 gcc_max = max(gcc_max, b[1])
322 elif b[0] == 'llvm_toolchain':
323 llvm_max = max(llvm_max, b[1])
324 else:
325 fp.write('%s,%d\n' % (b[0], b[1]))
326 if gcc_max > 0:
327 fp.write('gcc_toolchain,%d\n' % gcc_max)
328 if llvm_max > 0:
329 fp.write('llvm_toolchain,%d\n' % llvm_max)
330
331
332def GetBuilds():
333 """Read build-data.txt to determine values for current report."""
334
335 # Read the values of the last builds used to generate a report, and
336 # increment them appropriately, to get values for generating the
337 # current report. (See comments in UpdateBuilds).
338 with open(BUILD_DATA_FILE, 'r') as fp:
339 lines = fp.readlines()
340
341 builds = []
342 for l in lines:
343 l = l.rstrip()
344 words = l.split(',')
345 builder = words[0]
346 build = int(words[1])
347 builds.append((builder, build + 1))
348 # NOTE: We are assuming here that there are always 2 daily builds in
349 # each of the rotating builders. I am not convinced this is a valid
350 # assumption.
351 if builder == 'gcc_toolchain' or builder == 'llvm_toolchain':
352 builds.append((builder, build + 2))
353
354 return builds
355
356
357def RecordFailures(failure_dict, platform, suite, builder, int_date, log_file,
358 build_num, failed):
359 """Read and update the stored data about test failures."""
360
361 # Get the dictionary for this particular test suite from the failures
362 # dictionary.
363 suite_dict = failure_dict[suite]
364
365 # Read in the entire log file for this test/build.
366 with open(log_file, 'r') as in_file:
367 lines = in_file.readlines()
368
369 # Update the entries in the failure dictionary for each test within this suite
370 # that failed.
371 for test in failed:
372 # Check to see if there is already an entry in the suite dictionary for this
373 # test; if so use that, otherwise create a new entry.
374 if test in suite_dict:
375 test_dict = suite_dict[test]
376 else:
377 test_dict = dict()
378 # Parse the lines from the log file, looking for lines that indicate this
379 # test failed.
380 msg = ''
381 for l in lines:
382 words = l.split()
383 if len(words) < 3:
384 continue
385 if ((words[0] == test and words[1] == 'ERROR:') or
386 (words[0] == 'provision' and words[1] == 'FAIL:')):
387 words = words[2:]
388 # Get the error message for the failure.
389 msg = ' '.join(words)
390 if not msg:
391 msg = 'Unknown_Error'
392
393 # Look for an existing entry for this error message in the test dictionary.
394 # If found use that, otherwise create a new entry for this error message.
395 if msg in test_dict:
396 error_list = test_dict[msg]
397 else:
398 error_list = list()
399 # Create an entry for this new failure
400 new_item = [int_date, platform, builder, build_num]
401 # Add this failure to the error list if it's not already there.
402 if new_item not in error_list:
403 error_list.append([int_date, platform, builder, build_num])
404 # Sort the error list by date.
405 error_list.sort(key=lambda x: x[0])
406 # Calculate the earliest date to save; delete records for older failures.
407 earliest_date = int_date - MAX_SAVE_RECORDS
408 i = 0
Caroline Ticee02e9f82016-12-01 13:14:41 -0800409 while i < len(error_list) and error_list[i][0] <= earliest_date:
Caroline Tice48462062016-11-18 16:49:00 -0800410 i += 1
411 if i > 0:
412 error_list = error_list[i:]
413 # Save the error list in the test's dictionary, keyed on error_msg.
414 test_dict[msg] = error_list
415
416 # Save the updated test dictionary in the test_suite dictionary.
417 suite_dict[test] = test_dict
418
419 # Save the updated test_suite dictionary in the failure dictionary.
420 failure_dict[suite] = suite_dict
421
422
423def ParseLogFile(log_file, test_data_dict, failure_dict, test, builder,
424 build_num, build_link):
425 """Parse the log file from the given builder, build_num and test.
426
427 Also adds the results for this test to our test results dictionary,
428 and calls RecordFailures, to update our test failure data.
429 """
430
431 lines = []
432 with open(log_file, 'r') as infile:
433 lines = infile.readlines()
434
435 passed = {}
436 failed = {}
437 not_run = {}
438 date = ''
439 status = ''
440 board = ''
441 num_provision_errors = 0
442 build_ok = True
443 afe_line = ''
444
445 for line in lines:
446 if line.rstrip() == '<title>404 Not Found</title>':
447 print('Warning: File for %s (build number %d), %s was not found.' %
448 (builder, build_num, test))
449 build_ok = False
450 break
451 if '[ PASSED ]' in line:
452 test_name = line.split()[0]
453 if test_name != 'Suite':
454 passed[test_name] = True
455 elif '[ FAILED ]' in line:
456 test_name = line.split()[0]
457 if test_name == 'provision':
458 num_provision_errors += 1
459 not_run[test_name] = True
460 elif test_name != 'Suite':
461 failed[test_name] = True
462 elif line.startswith('started: '):
463 date = line.rstrip()
464 date = date[9:]
465 date_obj = time.strptime(date, '%a %b %d %H:%M:%S %Y')
466 int_date = (
467 date_obj.tm_year * 10000 + date_obj.tm_mon * 100 + date_obj.tm_mday)
468 date = time.strftime('%a %b %d %Y', date_obj)
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800469 elif not status and line.startswith('status: '):
Caroline Tice48462062016-11-18 16:49:00 -0800470 status = line.rstrip()
471 words = status.split(':')
472 status = words[-1]
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800473 elif line.find('Suite passed with a warning') != -1:
474 status = 'WARNING'
Caroline Tice48462062016-11-18 16:49:00 -0800475 elif line.startswith('@@@STEP_LINK@Link to suite@'):
476 afe_line = line.rstrip()
477 words = afe_line.split('@')
478 for w in words:
479 if w.startswith('http'):
480 afe_line = w
481 afe_line = afe_line.replace('&amp;', '&')
482 elif 'INFO: RunCommand:' in line:
483 words = line.split()
484 for i in range(0, len(words) - 1):
485 if words[i] == '--board':
486 board = words[i + 1]
487
488 test_dict = test_data_dict[test]
489 test_list = test_dict['tests']
490
491 if build_ok:
492 for t in test_list:
493 if not t in passed and not t in failed:
494 not_run[t] = True
495
496 total_pass = len(passed)
497 total_fail = len(failed)
498 total_notrun = len(not_run)
499
500 else:
501 total_pass = 0
502 total_fail = 0
503 total_notrun = 0
504 status = 'Not found.'
505 if not build_ok:
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800506 return [], date, board, 0, ' '
Caroline Tice48462062016-11-18 16:49:00 -0800507
508 build_dict = dict()
509 build_dict['id'] = build_num
510 build_dict['builder'] = builder
511 build_dict['date'] = date
512 build_dict['build_link'] = build_link
513 build_dict['total_pass'] = total_pass
514 build_dict['total_fail'] = total_fail
515 build_dict['total_not_run'] = total_notrun
516 build_dict['afe_job_link'] = afe_line
517 build_dict['provision_errors'] = num_provision_errors
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800518 if status.strip() == 'SUCCESS':
519 build_dict['color'] = 'green '
520 elif status.strip() == 'FAILURE':
521 build_dict['color'] = ' red '
522 elif status.strip() == 'WARNING':
523 build_dict['color'] = 'orange'
524 else:
525 build_dict['color'] = ' '
Caroline Tice48462062016-11-18 16:49:00 -0800526
527 # Use YYYYMMDD (integer) as the build record key
528 if build_ok:
529 if board in test_dict:
530 board_dict = test_dict[board]
531 else:
532 board_dict = dict()
533 board_dict[int_date] = build_dict
534
535 # Only keep the last 5 records (based on date)
536 keys_list = board_dict.keys()
537 if len(keys_list) > MAX_SAVE_RECORDS:
538 min_key = min(keys_list)
539 del board_dict[min_key]
540
541 # Make sure changes get back into the main dictionary
542 test_dict[board] = board_dict
543 test_data_dict[test] = test_dict
544
545 if len(failed) > 0:
546 RecordFailures(failure_dict, board, test, builder, int_date, log_file,
547 build_num, failed)
548
549 summary_result = '[%2d/ %2d/ %2d]' % (total_pass, total_fail, total_notrun)
550
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800551 return summary_result, date, board, int_date, build_dict['color']
Caroline Tice48462062016-11-18 16:49:00 -0800552
553
554def DownloadLogFile(builder, buildnum, test, test_family):
555
556 ce = command_executer.GetCommandExecuter()
557 os.system('mkdir -p %s/%s/%s' % (DOWNLOAD_DIR, builder, test))
558 if builder == 'gcc_toolchain' or builder == 'llvm_toolchain':
559 source = ('https://uberchromegw.corp.google.com/i/chromiumos.tryserver'
560 '/builders/%s/builds/%d/steps/%s%%20%%5B%s%%5D/logs/stdio' %
561 (builder, buildnum, test_family, test))
562 build_link = ('https://uberchromegw.corp.google.com/i/chromiumos.tryserver'
563 '/builders/%s/builds/%d' % (builder, buildnum))
564 else:
565 source = ('https://uberchromegw.corp.google.com/i/chromeos/builders/%s/'
566 'builds/%d/steps/%s%%20%%5B%s%%5D/logs/stdio' %
567 (builder, buildnum, test_family, test))
568 build_link = ('https://uberchromegw.corp.google.com/i/chromeos/builders/%s'
569 '/builds/%d' % (builder, buildnum))
570
571 target = '%s/%s/%s/%d' % (DOWNLOAD_DIR, builder, test, buildnum)
572 if not os.path.isfile(target) or os.path.getsize(target) == 0:
573 cmd = 'sso_client %s > %s' % (source, target)
574 status = ce.RunCommand(cmd)
575 if status != 0:
576 return '', ''
577
578 return target, build_link
579
580
Manoj Gupta63824522016-12-14 11:05:18 -0800581# Check for prodaccess.
582def CheckProdAccess():
583 status, output, _ = command_executer.GetCommandExecuter().RunCommandWOutput(
584 'prodcertstatus')
585 if status != 0:
586 return False
587 # Verify that status is not expired
588 if 'expires' in output:
589 return True
590 return False
591
592
Caroline Tice48462062016-11-18 16:49:00 -0800593def Main():
594 """Main function for this script."""
595
596 test_data_dict = dict()
597 failure_dict = dict()
Manoj Gupta63824522016-12-14 11:05:18 -0800598
599 prod_access = CheckProdAccess()
600 if not prod_access:
601 print('ERROR: Please run prodaccess first.')
602 return
603
Caroline Tice48462062016-11-18 16:49:00 -0800604 with open('%s/waterfall-test-data.json' % DATA_DIR, 'r') as input_file:
605 test_data_dict = json.load(input_file)
606
607 with open('%s/test-failure-data.json' % DATA_DIR, 'r') as fp:
608 failure_dict = json.load(fp)
609
610 builds = GetBuilds()
611
612 waterfall_report_dict = dict()
613 rotating_report_dict = dict()
614 int_date = 0
615 for test_desc in TESTS:
616 test, test_family = test_desc
617 for build in builds:
618 (builder, buildnum) = build
619 if test.startswith('kernel') and 'llvm' in builder:
620 continue
621 if 'x86' in builder and not test.startswith('bvt'):
622 continue
623 target, build_link = DownloadLogFile(builder, buildnum, test, test_family)
624
625 if os.path.exists(target):
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800626 test_summary, report_date, board, tmp_date, color = ParseLogFile(
Caroline Tice48462062016-11-18 16:49:00 -0800627 target, test_data_dict, failure_dict, test, builder, buildnum,
628 build_link)
629
630 if tmp_date != 0:
631 int_date = tmp_date
632
633 if builder in ROTATING_BUILDERS:
634 UpdateReport(rotating_report_dict, builder, test, report_date,
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800635 build_link, test_summary, board, color)
Caroline Tice48462062016-11-18 16:49:00 -0800636 else:
637 UpdateReport(waterfall_report_dict, builder, test, report_date,
Caroline Ticef0ad65c2016-11-29 10:40:23 -0800638 build_link, test_summary, board, color)
Caroline Tice48462062016-11-18 16:49:00 -0800639
Caroline Ticee02e9f82016-12-01 13:14:41 -0800640 PruneOldFailures(failure_dict, int_date)
641
Caroline Tice48462062016-11-18 16:49:00 -0800642 if waterfall_report_dict:
Caroline Ticee02e9f82016-12-01 13:14:41 -0800643 main_report = GenerateWaterfallReport(waterfall_report_dict, failure_dict,
644 'main', int_date)
645 EmailReport(main_report, 'Main', format_date(int_date))
646 shutil.copy(main_report, ARCHIVE_DIR)
Caroline Tice48462062016-11-18 16:49:00 -0800647 if rotating_report_dict:
Caroline Ticee02e9f82016-12-01 13:14:41 -0800648 rotating_report = GenerateWaterfallReport(rotating_report_dict,
649 failure_dict, 'rotating',
650 int_date)
651 EmailReport(rotating_report, 'Rotating', format_date(int_date))
652 shutil.copy(rotating_report, ARCHIVE_DIR)
Caroline Tice48462062016-11-18 16:49:00 -0800653
654 with open('%s/waterfall-test-data.json' % DATA_DIR, 'w') as out_file:
655 json.dump(test_data_dict, out_file, indent=2)
656
657 with open('%s/test-failure-data.json' % DATA_DIR, 'w') as out_file:
658 json.dump(failure_dict, out_file, indent=2)
659
660 UpdateBuilds(builds)
661
662
663if __name__ == '__main__':
664 Main()
665 sys.exit(0)