aboutsummaryrefslogtreecommitdiff
path: root/deprecated/generate-waterfall-reports.py
diff options
context:
space:
mode:
Diffstat (limited to 'deprecated/generate-waterfall-reports.py')
-rwxr-xr-xdeprecated/generate-waterfall-reports.py853
1 files changed, 0 insertions, 853 deletions
diff --git a/deprecated/generate-waterfall-reports.py b/deprecated/generate-waterfall-reports.py
deleted file mode 100755
index a67cd6ca..00000000
--- a/deprecated/generate-waterfall-reports.py
+++ /dev/null
@@ -1,853 +0,0 @@
-#!/usr/bin/env python2
-"""Generate summary report for ChromeOS toolchain waterfalls."""
-
-# Desired future features (to be added):
-# - arguments to allow generating only the main waterfall report,
-# or only the rotating builder reports, or only the failures
-# report; or the waterfall reports without the failures report.
-# - Better way of figuring out which dates/builds to generate
-# reports for: probably an argument specifying a date or a date
-# range, then use something like the new buildbot utils to
-# query the build logs to find the right build numbers for the
-# builders for the specified dates.
-# - Store/get the json/data files in mobiletc-prebuild's x20 area.
-# - Update data in json file to reflect, for each testsuite, which
-# tests are not expected to run on which boards; update this
-# script to use that data appropriately.
-# - Make sure user's prodaccess is up-to-date before trying to use
-# this script.
-# - Add some nice formatting/highlighting to reports.
-
-from __future__ import print_function
-
-import argparse
-import getpass
-import json
-import os
-import re
-import shutil
-import sys
-import time
-
-from cros_utils import command_executer
-
-# All the test suites whose data we might want for the reports.
-TESTS = (('bvt-inline', 'HWTest'), ('bvt-cq', 'HWTest'), ('security', 'HWTest'),
- ('kernel_daily_regression', 'HWTest'), ('kernel_daily_benchmarks',
- 'HWTest'),)
-
-# The main waterfall builders, IN THE ORDER IN WHICH WE WANT THEM
-# LISTED IN THE REPORT.
-WATERFALL_BUILDERS = [
- 'amd64-llvm-next-toolchain',
- 'arm-llvm-next-toolchain',
- 'arm64-llvm-next-toolchain',
-]
-
-DATA_DIR = '/google/data/rw/users/mo/mobiletc-prebuild/waterfall-report-data/'
-ARCHIVE_DIR = '/google/data/rw/users/mo/mobiletc-prebuild/waterfall-reports/'
-DOWNLOAD_DIR = '/tmp/waterfall-logs'
-MAX_SAVE_RECORDS = 7
-BUILD_DATA_FILE = '%s/build-data.txt' % DATA_DIR
-GCC_ROTATING_BUILDER = 'gcc_toolchain'
-LLVM_ROTATING_BUILDER = 'llvm_next_toolchain'
-ROTATING_BUILDERS = [GCC_ROTATING_BUILDER, LLVM_ROTATING_BUILDER]
-
-# For int-to-string date conversion. Note, the index of the month in this
-# list needs to correspond to the month's integer value. i.e. 'Sep' must
-# be as MONTHS[9].
-MONTHS = [
- '', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct',
- 'Nov', 'Dec'
-]
-
-
-def format_date(int_date):
- """Convert an integer date to a string date. YYYYMMDD -> YYYY-MMM-DD"""
-
- if int_date == 0:
- return 'today'
-
- tmp_date = int_date
- day = tmp_date % 100
- tmp_date = tmp_date / 100
- month = tmp_date % 100
- year = tmp_date / 100
-
- month_str = MONTHS[month]
- date_str = '%d-%s-%d' % (year, month_str, day)
- return date_str
-
-
-def EmailReport(report_file, report_type, date, email_to):
- subject = '%s Waterfall Summary report, %s' % (report_type, date)
- sendgmr_path = '/google/data/ro/projects/gws-sre/sendgmr'
- command = ('%s --to=%s --subject="%s" --body_file=%s' %
- (sendgmr_path, email_to, subject, report_file))
- command_executer.GetCommandExecuter().RunCommand(command)
-
-
-def PruneOldFailures(failure_dict, int_date):
- earliest_date = int_date - MAX_SAVE_RECORDS
- for suite in failure_dict:
- suite_dict = failure_dict[suite]
- test_keys_to_remove = []
- for test in suite_dict:
- test_dict = suite_dict[test]
- msg_keys_to_remove = []
- for msg in test_dict:
- fails = test_dict[msg]
- i = 0
- while i < len(fails) and fails[i][0] <= earliest_date:
- i += 1
- new_fails = fails[i:]
- test_dict[msg] = new_fails
- if len(new_fails) == 0:
- msg_keys_to_remove.append(msg)
-
- for k in msg_keys_to_remove:
- del test_dict[k]
-
- suite_dict[test] = test_dict
- if len(test_dict) == 0:
- test_keys_to_remove.append(test)
-
- for k in test_keys_to_remove:
- del suite_dict[k]
-
- failure_dict[suite] = suite_dict
-
-
-def GetBuildID(build_bot, date):
- """Get the build id for a build_bot at a given date."""
- day = '{day:02d}'.format(day=date % 100)
- mon = MONTHS[date / 100 % 100]
- date_string = mon + ' ' + day
- if build_bot in WATERFALL_BUILDERS:
- url = 'https://uberchromegw.corp.google.com/i/chromeos/' + \
- 'builders/%s?numbuilds=200' % build_bot
- if build_bot in ROTATING_BUILDERS:
- url = 'https://uberchromegw.corp.google.com/i/chromiumos.tryserver/' + \
- 'builders/%s?numbuilds=200' % build_bot
- command = 'sso_client %s' % url
- retval = 1
- retry_time = 3
- while retval and retry_time:
- retval, output, _ = \
- command_executer.GetCommandExecuter().RunCommandWOutput(command, \
- print_to_console=False)
- retry_time -= 1
-
- if retval:
- return []
-
- out = output.split('\n')
- line_num = 0
- build_id = []
- # Parse the output like this
- # <td>Dec 14 10:55</td>
- # <td class="revision">??</td>
- # <td failure</td><td><a href="../builders/gcc_toolchain/builds/109">#109</a>
- while line_num < len(out):
- if date_string in out[line_num]:
- if line_num + 2 < len(out):
- build_num_line = out[line_num + 2]
- raw_num = re.findall(r'builds/\d+', build_num_line)
- # raw_num is ['builds/109'] in the example.
- if raw_num:
- build_id.append(int(raw_num[0].split('/')[1]))
- line_num += 1
- return build_id
-
-
-def GenerateFailuresReport(fail_dict, date):
- filename = 'waterfall_report.failures.%s.txt' % date
- date_string = format_date(date)
- with open(filename, 'w') as out_file:
- # Write failure report section.
- out_file.write('\n\nSummary of Test Failures as of %s\n\n' % date_string)
-
- # We want to sort the errors and output them in order of the ones that occur
- # most often. So we have to collect the data about all of them, then sort
- # it.
- error_groups = []
- for suite in fail_dict:
- suite_dict = fail_dict[suite]
- if suite_dict:
- for test in suite_dict:
- test_dict = suite_dict[test]
- for err_msg in test_dict:
- err_list = test_dict[err_msg]
- sorted_list = sorted(err_list, key=lambda x: x[0], reverse=True)
- err_group = [len(sorted_list), suite, test, err_msg, sorted_list]
- error_groups.append(err_group)
-
- # Sort the errors by the number of errors of each type. Then output them in
- # order.
- sorted_errors = sorted(error_groups, key=lambda x: x[0], reverse=True)
- for i in range(0, len(sorted_errors)):
- err_group = sorted_errors[i]
- suite = err_group[1]
- test = err_group[2]
- err_msg = err_group[3]
- err_list = err_group[4]
- out_file.write('Suite: %s\n' % suite)
- out_file.write(' %s (%d failures)\n' % (test, len(err_list)))
- out_file.write(' (%s)\n' % err_msg)
- for i in range(0, len(err_list)):
- err = err_list[i]
- out_file.write(' %s, %s, %s\n' % (format_date(err[0]), err[1],
- err[2]))
- out_file.write('\n')
-
- print('Report generated in %s.' % filename)
- return filename
-
-
-def GenerateWaterfallReport(report_dict, fail_dict, waterfall_type, date,
- omit_failures):
- """Write out the actual formatted report."""
-
- filename = 'waterfall_report.%s_waterfall.%s.txt' % (waterfall_type, date)
-
- date_string = ''
- date_list = report_dict['date']
- num_dates = len(date_list)
- i = 0
- for d in date_list:
- date_string += d
- if i < num_dates - 1:
- date_string += ', '
- i += 1
-
- if waterfall_type == 'main':
- report_list = WATERFALL_BUILDERS
- else:
- report_list = report_dict.keys()
-
- with open(filename, 'w') as out_file:
- # Write Report Header
- out_file.write('\nStatus of %s Waterfall Builds from %s\n\n' %
- (waterfall_type, date_string))
- out_file.write(' '
- ' kernel kernel\n')
- out_file.write(' Build bvt- bvt-cq '
- ' security daily daily\n')
- out_file.write(' status inline '
- ' regression benchmarks\n')
- out_file.write(' [P/ F/ DR]* [P/ F /DR]* '
- '[P/ F/ DR]* [P/ F/ DR]* [P/ F/ DR]*\n\n')
-
- # Write daily waterfall status section.
- for i in range(0, len(report_list)):
- builder = report_list[i]
- if builder == 'date':
- continue
-
- if builder not in report_dict:
- out_file.write('Unable to find information for %s.\n\n' % builder)
- continue
-
- build_dict = report_dict[builder]
- status = build_dict.get('build_status', 'bad')
- inline = build_dict.get('bvt-inline', '[??/ ?? /??]')
- cq = build_dict.get('bvt-cq', '[??/ ?? /??]')
- inline_color = build_dict.get('bvt-inline-color', '')
- cq_color = build_dict.get('bvt-cq-color', '')
- if 'x86' not in builder:
- security = build_dict.get('security', '[??/ ?? /??]')
- security_color = build_dict.get('security-color', '')
- if 'gcc' in builder:
- regression = build_dict.get('kernel_daily_regression', '[??/ ?? /??]')
- bench = build_dict.get('kernel_daily_benchmarks', '[??/ ?? /??]')
- regression_color = build_dict.get('kernel_daily_regression-color', '')
- bench_color = build_dict.get('kernel_daily_benchmarks-color', '')
- out_file.write(' %6s %6s'
- ' %6s %6s %6s\n' %
- (inline_color, cq_color, security_color,
- regression_color, bench_color))
- out_file.write('%25s %3s %s %s %s %s %s\n' %
- (builder, status, inline, cq, security, regression,
- bench))
- else:
- out_file.write(' %6s %6s'
- ' %6s\n' % (inline_color, cq_color,
- security_color))
- out_file.write('%25s %3s %s %s %s\n' % (builder, status, inline, cq,
- security))
- else:
- out_file.write(' %6s %6s\n' %
- (inline_color, cq_color))
- out_file.write('%25s %3s %s %s\n' % (builder, status, inline, cq))
- if 'build_link' in build_dict:
- out_file.write('%s\n\n' % build_dict['build_link'])
-
- out_file.write('\n\n*P = Number of tests in suite that Passed; F = '
- 'Number of tests in suite that Failed; DR = Number of tests'
- ' in suite that Didn\'t Run.\n')
-
- if omit_failures:
- print('Report generated in %s.' % filename)
- return filename
-
- # Write failure report section.
- out_file.write('\n\nSummary of Test Failures as of %s\n\n' % date_string)
-
- # We want to sort the errors and output them in order of the ones that occur
- # most often. So we have to collect the data about all of them, then sort
- # it.
- error_groups = []
- for suite in fail_dict:
- suite_dict = fail_dict[suite]
- if suite_dict:
- for test in suite_dict:
- test_dict = suite_dict[test]
- for err_msg in test_dict:
- err_list = test_dict[err_msg]
- sorted_list = sorted(err_list, key=lambda x: x[0], reverse=True)
- err_group = [len(sorted_list), suite, test, err_msg, sorted_list]
- error_groups.append(err_group)
-
- # Sort the errors by the number of errors of each type. Then output them in
- # order.
- sorted_errors = sorted(error_groups, key=lambda x: x[0], reverse=True)
- for i in range(0, len(sorted_errors)):
- err_group = sorted_errors[i]
- suite = err_group[1]
- test = err_group[2]
- err_msg = err_group[3]
- err_list = err_group[4]
- out_file.write('Suite: %s\n' % suite)
- out_file.write(' %s (%d failures)\n' % (test, len(err_list)))
- out_file.write(' (%s)\n' % err_msg)
- for i in range(0, len(err_list)):
- err = err_list[i]
- out_file.write(' %s, %s, %s\n' % (format_date(err[0]), err[1],
- err[2]))
- out_file.write('\n')
-
- print('Report generated in %s.' % filename)
- return filename
-
-
-def UpdateReport(report_dict, builder, test, report_date, build_link,
- test_summary, board, color):
- """Update the data in our report dictionary with current test's data."""
-
- if 'date' not in report_dict:
- report_dict['date'] = [report_date]
- elif report_date not in report_dict['date']:
- # It is possible that some of the builders started/finished on different
- # days, so we allow for multiple dates in the reports.
- report_dict['date'].append(report_date)
-
- build_key = ''
- if builder == GCC_ROTATING_BUILDER:
- build_key = '%s-gcc-toolchain' % board
- elif builder == LLVM_ROTATING_BUILDER:
- build_key = '%s-llvm-next-toolchain' % board
- else:
- build_key = builder
-
- if build_key not in report_dict.keys():
- build_dict = dict()
- else:
- build_dict = report_dict[build_key]
-
- if 'build_link' not in build_dict:
- build_dict['build_link'] = build_link
-
- if 'date' not in build_dict:
- build_dict['date'] = report_date
-
- if 'board' in build_dict and build_dict['board'] != board:
- raise RuntimeError(
- 'Error: Two different boards (%s,%s) in one build (%s)!' %
- (board, build_dict['board'], build_link))
- build_dict['board'] = board
-
- color_key = '%s-color' % test
- build_dict[color_key] = color
-
- # Check to see if we already have a build status for this build_key
- status = ''
- if 'build_status' in build_dict.keys():
- # Use current build_status, unless current test failed (see below).
- status = build_dict['build_status']
-
- if not test_summary:
- # Current test data was not available, so something was bad with build.
- build_dict['build_status'] = 'bad'
- build_dict[test] = '[ no data ]'
- else:
- build_dict[test] = test_summary
- if not status:
- # Current test ok; no other data, so assume build was ok.
- build_dict['build_status'] = 'ok'
-
- report_dict[build_key] = build_dict
-
-
-def UpdateBuilds(builds):
- """Update the data in our build-data.txt file."""
-
- # The build data file records the last build number for which we
- # generated a report. When we generate the next report, we read
- # this data and increment it to get the new data; when we finish
- # generating the reports, we write the updated values into this file.
- # NOTE: One side effect of doing this at the end: If the script
- # fails in the middle of generating a report, this data does not get
- # updated.
- with open(BUILD_DATA_FILE, 'w') as fp:
- gcc_max = 0
- llvm_max = 0
- for b in builds:
- if b[0] == GCC_ROTATING_BUILDER:
- gcc_max = max(gcc_max, b[1])
- elif b[0] == LLVM_ROTATING_BUILDER:
- llvm_max = max(llvm_max, b[1])
- else:
- fp.write('%s,%d\n' % (b[0], b[1]))
- if gcc_max > 0:
- fp.write('%s,%d\n' % (GCC_ROTATING_BUILDER, gcc_max))
- if llvm_max > 0:
- fp.write('%s,%d\n' % (LLVM_ROTATING_BUILDER, llvm_max))
-
-
-def GetBuilds(date=0):
- """Get build id from builds."""
-
- # If date is set, get the build id from waterfall.
- builds = []
-
- if date:
- for builder in WATERFALL_BUILDERS + ROTATING_BUILDERS:
- build_ids = GetBuildID(builder, date)
- for build_id in build_ids:
- builds.append((builder, build_id))
- return builds
-
- # If date is not set, we try to get the most recent builds.
- # Read the values of the last builds used to generate a report, and
- # increment them appropriately, to get values for generating the
- # current report. (See comments in UpdateBuilds).
- with open(BUILD_DATA_FILE, 'r') as fp:
- lines = fp.readlines()
-
- for l in lines:
- l = l.rstrip()
- words = l.split(',')
- builder = words[0]
- build = int(words[1])
- builds.append((builder, build + 1))
- # NOTE: We are assuming here that there are always 2 daily builds in
- # each of the rotating builders. I am not convinced this is a valid
- # assumption.
- if builder in ROTATING_BUILDERS:
- builds.append((builder, build + 2))
-
- return builds
-
-
-def RecordFailures(failure_dict, platform, suite, builder, int_date, log_file,
- build_num, failed):
- """Read and update the stored data about test failures."""
-
- # Get the dictionary for this particular test suite from the failures
- # dictionary.
- suite_dict = failure_dict[suite]
-
- # Read in the entire log file for this test/build.
- with open(log_file, 'r') as in_file:
- lines = in_file.readlines()
-
- # Update the entries in the failure dictionary for each test within this suite
- # that failed.
- for test in failed:
- # Check to see if there is already an entry in the suite dictionary for this
- # test; if so use that, otherwise create a new entry.
- if test in suite_dict:
- test_dict = suite_dict[test]
- else:
- test_dict = dict()
- # Parse the lines from the log file, looking for lines that indicate this
- # test failed.
- msg = ''
- for l in lines:
- words = l.split()
- if len(words) < 3:
- continue
- if ((words[0] == test and words[1] == 'ERROR:') or
- (words[0] == 'provision' and words[1] == 'FAIL:')):
- words = words[2:]
- # Get the error message for the failure.
- msg = ' '.join(words)
- if not msg:
- msg = 'Unknown_Error'
-
- # Look for an existing entry for this error message in the test dictionary.
- # If found use that, otherwise create a new entry for this error message.
- if msg in test_dict:
- error_list = test_dict[msg]
- else:
- error_list = list()
- # Create an entry for this new failure
- new_item = [int_date, platform, builder, build_num]
- # Add this failure to the error list if it's not already there.
- if new_item not in error_list:
- error_list.append([int_date, platform, builder, build_num])
- # Sort the error list by date.
- error_list.sort(key=lambda x: x[0])
- # Calculate the earliest date to save; delete records for older failures.
- earliest_date = int_date - MAX_SAVE_RECORDS
- i = 0
- while i < len(error_list) and error_list[i][0] <= earliest_date:
- i += 1
- if i > 0:
- error_list = error_list[i:]
- # Save the error list in the test's dictionary, keyed on error_msg.
- test_dict[msg] = error_list
-
- # Save the updated test dictionary in the test_suite dictionary.
- suite_dict[test] = test_dict
-
- # Save the updated test_suite dictionary in the failure dictionary.
- failure_dict[suite] = suite_dict
-
-
-def ParseLogFile(log_file, test_data_dict, failure_dict, test, builder,
- build_num, build_link):
- """Parse the log file from the given builder, build_num and test.
-
- Also adds the results for this test to our test results dictionary,
- and calls RecordFailures, to update our test failure data.
- """
-
- print('Parsing file %s' % log_file)
- lines = []
- with open(log_file, 'r') as infile:
- lines = infile.readlines()
-
- passed = {}
- failed = {}
- not_run = {}
- date = ''
- status = ''
- board = ''
- num_provision_errors = 0
- build_ok = True
- afe_line = ''
-
- for line in lines:
- if line.rstrip() == '<title>404 Not Found</title>':
- print('Warning: File for %s (build number %d), %s was not found.' %
- (builder, build_num, test))
- build_ok = False
- break
- if '[ PASSED ]' in line:
- test_name = line.split()[0]
- if test_name != 'Suite':
- passed[test_name] = True
- elif '[ FAILED ]' in line:
- test_name = line.split()[0]
- if test_name == 'provision':
- num_provision_errors += 1
- not_run[test_name] = True
- elif test_name != 'Suite':
- failed[test_name] = True
- elif line.startswith('started: '):
- date = line.rstrip()
- date = date[9:]
- date_obj = time.strptime(date, '%a %b %d %H:%M:%S %Y')
- int_date = (
- date_obj.tm_year * 10000 + date_obj.tm_mon * 100 + date_obj.tm_mday)
- date = time.strftime('%a %b %d %Y', date_obj)
- elif not status and line.startswith('status: '):
- status = line.rstrip()
- words = status.split(':')
- status = words[-1]
- elif line.find('Suite passed with a warning') != -1:
- status = 'WARNING'
- elif line.startswith('@@@STEP_LINK@Link to suite@'):
- afe_line = line.rstrip()
- words = afe_line.split('@')
- for w in words:
- if w.startswith('http'):
- afe_line = w
- afe_line = afe_line.replace('&amp;', '&')
- elif 'INFO: RunCommand:' in line:
- words = line.split()
- for i in range(0, len(words) - 1):
- if words[i] == '--board':
- board = words[i + 1]
-
- test_dict = test_data_dict[test]
- test_list = test_dict['tests']
-
- if build_ok:
- for t in test_list:
- if not t in passed and not t in failed:
- not_run[t] = True
-
- total_pass = len(passed)
- total_fail = len(failed)
- total_notrun = len(not_run)
-
- else:
- total_pass = 0
- total_fail = 0
- total_notrun = 0
- status = 'Not found.'
- if not build_ok:
- return [], date, board, 0, ' '
-
- build_dict = dict()
- build_dict['id'] = build_num
- build_dict['builder'] = builder
- build_dict['date'] = date
- build_dict['build_link'] = build_link
- build_dict['total_pass'] = total_pass
- build_dict['total_fail'] = total_fail
- build_dict['total_not_run'] = total_notrun
- build_dict['afe_job_link'] = afe_line
- build_dict['provision_errors'] = num_provision_errors
- if status.strip() == 'SUCCESS':
- build_dict['color'] = 'green '
- elif status.strip() == 'FAILURE':
- build_dict['color'] = ' red '
- elif status.strip() == 'WARNING':
- build_dict['color'] = 'orange'
- else:
- build_dict['color'] = ' '
-
- # Use YYYYMMDD (integer) as the build record key
- if build_ok:
- if board in test_dict:
- board_dict = test_dict[board]
- else:
- board_dict = dict()
- board_dict[int_date] = build_dict
-
- # Only keep the last 5 records (based on date)
- keys_list = board_dict.keys()
- if len(keys_list) > MAX_SAVE_RECORDS:
- min_key = min(keys_list)
- del board_dict[min_key]
-
- # Make sure changes get back into the main dictionary
- test_dict[board] = board_dict
- test_data_dict[test] = test_dict
-
- if len(failed) > 0:
- RecordFailures(failure_dict, board, test, builder, int_date, log_file,
- build_num, failed)
-
- summary_result = '[%2d/ %2d/ %2d]' % (total_pass, total_fail, total_notrun)
-
- return summary_result, date, board, int_date, build_dict['color']
-
-
-def DownloadLogFile(builder, buildnum, test, test_family):
-
- ce = command_executer.GetCommandExecuter()
- os.system('mkdir -p %s/%s/%s' % (DOWNLOAD_DIR, builder, test))
- if builder in ROTATING_BUILDERS:
- source = ('https://uberchromegw.corp.google.com/i/chromiumos.tryserver'
- '/builders/%s/builds/%d/steps/%s%%20%%5B%s%%5D/logs/stdio' %
- (builder, buildnum, test_family, test))
- build_link = ('https://uberchromegw.corp.google.com/i/chromiumos.tryserver'
- '/builders/%s/builds/%d' % (builder, buildnum))
- else:
- source = ('https://uberchromegw.corp.google.com/i/chromeos/builders/%s/'
- 'builds/%d/steps/%s%%20%%5B%s%%5D/logs/stdio' %
- (builder, buildnum, test_family, test))
- build_link = ('https://uberchromegw.corp.google.com/i/chromeos/builders/%s'
- '/builds/%d' % (builder, buildnum))
-
- target = '%s/%s/%s/%d' % (DOWNLOAD_DIR, builder, test, buildnum)
- if not os.path.isfile(target) or os.path.getsize(target) == 0:
- cmd = 'sso_client %s > %s' % (source, target)
- status = ce.RunCommand(cmd)
- if status != 0:
- return '', ''
-
- return target, build_link
-
-
-# Check for prodaccess.
-def CheckProdAccess():
- status, output, _ = command_executer.GetCommandExecuter().RunCommandWOutput(
- 'prodcertstatus')
- if status != 0:
- return False
- # Verify that status is not expired
- if 'expires' in output:
- return True
- return False
-
-
-def ValidOptions(parser, options):
- too_many_options = False
- if options.main:
- if options.rotating or options.failures_report:
- too_many_options = True
- elif options.rotating and options.failures_report:
- too_many_options = True
-
- if too_many_options:
- parser.error('Can only specify one of --main, --rotating or'
- ' --failures_report.')
-
- conflicting_failure_options = False
- if options.failures_report and options.omit_failures:
- conflicting_failure_options = True
- parser.error('Cannot specify both --failures_report and --omit_failures.')
-
- email_ok = True
- if options.email and options.email.find('@') == -1:
- email_ok = False
- parser.error('"%s" is not a valid email address; it must contain "@..."' %
- options.email)
-
- return not too_many_options and not conflicting_failure_options and email_ok
-
-
-def Main(argv):
- """Main function for this script."""
- parser = argparse.ArgumentParser()
- parser.add_argument(
- '--main',
- dest='main',
- default=False,
- action='store_true',
- help='Generate report only for main waterfall '
- 'builders.')
- parser.add_argument(
- '--rotating',
- dest='rotating',
- default=False,
- action='store_true',
- help='Generate report only for rotating builders.')
- parser.add_argument(
- '--failures_report',
- dest='failures_report',
- default=False,
- action='store_true',
- help='Only generate the failures section of the report.')
- parser.add_argument(
- '--omit_failures',
- dest='omit_failures',
- default=False,
- action='store_true',
- help='Do not generate the failures section of the report.')
- parser.add_argument(
- '--no_update',
- dest='no_update',
- default=False,
- action='store_true',
- help='Run reports, but do not update the data files.')
- parser.add_argument(
- '--date',
- dest='date',
- default=0,
- type=int,
- help='The date YYYYMMDD of waterfall report.')
- parser.add_argument(
- '--email',
- dest='email',
- default='',
- help='Email address to use for sending the report.')
-
- options = parser.parse_args(argv)
-
- if not ValidOptions(parser, options):
- return 1
-
- main_only = options.main
- rotating_only = options.rotating
- failures_report = options.failures_report
- omit_failures = options.omit_failures
- date = options.date
-
- test_data_dict = dict()
- failure_dict = dict()
-
- prod_access = CheckProdAccess()
- if not prod_access:
- print('ERROR: Please run prodaccess first.')
- return
-
- with open('%s/waterfall-test-data.json' % DATA_DIR, 'r') as input_file:
- test_data_dict = json.load(input_file)
-
- with open('%s/test-failure-data.json' % DATA_DIR, 'r') as fp:
- failure_dict = json.load(fp)
-
- builds = GetBuilds(date)
-
- waterfall_report_dict = dict()
- rotating_report_dict = dict()
- int_date = 0
- for test_desc in TESTS:
- test, test_family = test_desc
- for build in builds:
- (builder, buildnum) = build
- if test.startswith('kernel') and 'llvm' in builder:
- continue
- if 'x86' in builder and not test.startswith('bvt'):
- continue
- target, build_link = DownloadLogFile(builder, buildnum, test, test_family)
-
- if os.path.exists(target):
- test_summary, report_date, board, tmp_date, color = ParseLogFile(
- target, test_data_dict, failure_dict, test, builder, buildnum,
- build_link)
- if not test_summary:
- continue
-
- if tmp_date != 0:
- int_date = tmp_date
-
- if builder in ROTATING_BUILDERS:
- UpdateReport(rotating_report_dict, builder, test, report_date,
- build_link, test_summary, board, color)
- else:
- UpdateReport(waterfall_report_dict, builder, test, report_date,
- build_link, test_summary, board, color)
-
- PruneOldFailures(failure_dict, int_date)
-
- if options.email:
- email_to = options.email
- else:
- email_to = getpass.getuser()
-
- if waterfall_report_dict and not rotating_only and not failures_report:
- main_report = GenerateWaterfallReport(waterfall_report_dict, failure_dict,
- 'main', int_date, omit_failures)
- EmailReport(main_report, 'Main', format_date(int_date), email_to)
- shutil.copy(main_report, ARCHIVE_DIR)
- if rotating_report_dict and not main_only and not failures_report:
- rotating_report = GenerateWaterfallReport(
- rotating_report_dict, failure_dict, 'rotating', int_date, omit_failures)
- EmailReport(rotating_report, 'Rotating', format_date(int_date), email_to)
- shutil.copy(rotating_report, ARCHIVE_DIR)
-
- if failures_report:
- failures_report = GenerateFailuresReport(failure_dict, int_date)
- EmailReport(failures_report, 'Failures', format_date(int_date), email_to)
- shutil.copy(failures_report, ARCHIVE_DIR)
-
- if not options.no_update:
- with open('%s/waterfall-test-data.json' % DATA_DIR, 'w') as out_file:
- json.dump(test_data_dict, out_file, indent=2)
-
- with open('%s/test-failure-data.json' % DATA_DIR, 'w') as out_file:
- json.dump(failure_dict, out_file, indent=2)
-
- UpdateBuilds(builds)
-
-
-if __name__ == '__main__':
- Main(sys.argv[1:])
- sys.exit(0)