Upstream Autotest merge.
As titled, a merge up to 93fc426ca133e775eb495f34d138fc57d92fb55e.
- Removes a bunch of deprecated code.
- Moves several private utilities into the private repo.
- Couple changes ported upstream and resynced.
BUG=None
TEST=In progress... will spin up new Autotest server and use
run_remote_tests for bvt, regression, smoke.
Change-Id: Id3e2ad529bb7b05f148e5d98aea46bb9ea828200
Reviewed-on: http://gerrit.chromium.org/gerrit/3350
Tested-by: Dale Curtis <dalecurtis@chromium.org>
Reviewed-by: Dale Curtis <dalecurtis@chromium.org>
diff --git a/client/tools/html_report.py b/client/tools/html_report.py
old mode 100644
new mode 100755
index d220ade..563a7a9
--- a/client/tools/html_report.py
+++ b/client/tools/html_report.py
@@ -1,6 +1,6 @@
#!/usr/bin/python
"""
-Script used to parse the test results and generate an HTML report.
+Module used to parse the autotest job results and generate an HTML report.
@copyright: (c)2005-2007 Matt Kruse (javascripttoolbox.com)
@copyright: Red Hat 2008-2009
@@ -27,7 +27,6 @@
text-decoration:none;
font:bold 2em/2em Arial, Helvetica, sans-serif;
text-transform:none;
- text-shadow: 2px 2px 2px #555;
text-align: left;
color:#555555;
border-bottom: 1px solid #555555;
@@ -37,7 +36,6 @@
text-decoration:none;
font:bold 16px Arial, Helvetica, sans-serif;
text-transform:uppercase;
- text-shadow: 2px 2px 2px #555;
text-align: left;
color:#555555;
margin-bottom:0;
@@ -1375,20 +1373,26 @@
"""
-#################################################################
-## This script gets kvm autotest results directory path as an ##
-## input and create a single html formatted result page. ##
-#################################################################
-
-stimelist = []
def make_html_file(metadata, results, tag, host, output_file_name, dirname):
+ """
+ Create HTML file contents for the job report, to stdout or filesystem.
+
+ @param metadata: Dictionary with Job metadata (tests, exec time, etc).
+ @param results: List with testcase results.
+ @param tag: Job tag.
+ @param host: Client hostname.
+ @param output_file_name: Output file name. If empty string, prints to
+ stdout.
+ @param dirname: Prefix for HTML links. If empty string, the HTML links
+ will be relative to the results dir.
+ """
html_prefix = """
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
-<title>KVM Autotest Results</title>
+<title>Autotest job execution results</title>
<style type="text/css">
%s
</style>
@@ -1407,14 +1411,13 @@
<body>
""" % (format_css, table_js, maketree_js)
-
if output_file_name:
output = open(output_file_name, "w")
else: #if no output file defined, print html file to console
output = sys.stdout
# create html page
print >> output, html_prefix
- print >> output, '<h2 id=\"page_title\">KVM Autotest Execution Report</h2>'
+ print >> output, '<h2 id=\"page_title\">Autotest job execution report</h2>'
# formating date and time to print
t = datetime.datetime.now()
@@ -1427,18 +1430,19 @@
total_failed = 0
total_passed = 0
for res in results:
- total_executed += 1
- if res['status'] == 'GOOD':
- total_passed += 1
- else:
- total_failed += 1
+ if results[res][2] != None:
+ total_executed += 1
+ if results[res][2]['status'] == 'GOOD':
+ total_passed += 1
+ else:
+ total_failed += 1
stat_str = 'No test cases executed'
if total_executed > 0:
failed_perct = int(float(total_failed)/float(total_executed)*100)
stat_str = ('From %d tests executed, %d have passed (%d%% failures)' %
(total_executed, total_passed, failed_perct))
- kvm_ver_str = metadata['kvmver']
+ kvm_ver_str = metadata.get('kvmver', None)
print >> output, '<table class="stats2">'
print >> output, '<tr><td>HOST</td><td>:</td><td>%s</td></tr>' % host
@@ -1446,10 +1450,10 @@
print >> output, '<tr><td>DATE</td><td>:</td><td>%s</td></tr>' % now.ctime()
print >> output, '<tr><td>STATS</td><td>:</td><td>%s</td></tr>'% stat_str
print >> output, '<tr><td></td><td></td><td></td></tr>'
- print >> output, '<tr><td>KVM VERSION</td><td>:</td><td>%s</td></tr>' % kvm_ver_str
+ if kvm_ver_str is not None:
+ print >> output, '<tr><td>KVM VERSION</td><td>:</td><td>%s</td></tr>' % kvm_ver_str
print >> output, '</table>'
-
## print test results
print >> output, '<br>'
print >> output, '<h2 id=\"page_sub_title\">Test Results</h2>'
@@ -1468,39 +1472,46 @@
<tbody>
"""
print >> output, result_table_prefix
- for res in results:
- print >> output, '<tr>'
- print >> output, '<td align="left">%s</td>' % res['time']
- print >> output, '<td align="left">%s</td>' % res['testcase']
- if res['status'] == 'GOOD':
- print >> output, '<td align=\"left\"><b><font color="#00CC00">PASS</font></b></td>'
- elif res['status'] == 'FAIL':
- print >> output, '<td align=\"left\"><b><font color="red">FAIL</font></b></td>'
- elif res['status'] == 'ERROR':
- print >> output, '<td align=\"left\"><b><font color="red">ERROR!</font></b></td>'
- else:
- print >> output, '<td align=\"left\">%s</td>' % res['status']
- # print exec time (seconds)
- print >> output, '<td align="left">%s</td>' % res['exec_time_sec']
- # print log only if test failed..
- if res['log']:
- #chop all '\n' from log text (to prevent html errors)
- rx1 = re.compile('(\s+)')
- log_text = rx1.sub(' ', res['log'])
+ def print_result(result, indent):
+ while result != []:
+ r = result.pop(0)
+ print r
+ res = results[r][2]
+ print >> output, '<tr>'
+ print >> output, '<td align="left">%s</td>' % res['time']
+ print >> output, '<td align="left" style="padding-left:%dpx">%s</td>' % (indent * 20, res['title'])
+ if res['status'] == 'GOOD':
+ print >> output, '<td align=\"left\"><b><font color="#00CC00">PASS</font></b></td>'
+ elif res['status'] == 'FAIL':
+ print >> output, '<td align=\"left\"><b><font color="red">FAIL</font></b></td>'
+ elif res['status'] == 'ERROR':
+ print >> output, '<td align=\"left\"><b><font color="red">ERROR!</font></b></td>'
+ else:
+ print >> output, '<td align=\"left\">%s</td>' % res['status']
+ # print exec time (seconds)
+ print >> output, '<td align="left">%s</td>' % res['exec_time_sec']
+ # print log only if test failed..
+ if res['log']:
+ #chop all '\n' from log text (to prevent html errors)
+ rx1 = re.compile('(\s+)')
+ log_text = rx1.sub(' ', res['log'])
- # allow only a-zA-Z0-9_ in html title name
- # (due to bug in MS-explorer)
- rx2 = re.compile('([^a-zA-Z_0-9])')
- updated_tag = rx2.sub('_', res['title'])
+ # allow only a-zA-Z0-9_ in html title name
+ # (due to bug in MS-explorer)
+ rx2 = re.compile('([^a-zA-Z_0-9])')
+ updated_tag = rx2.sub('_', res['title'])
- html_body_text = '<html><head><title>%s</title></head><body>%s</body></html>' % (str(updated_tag), log_text)
- print >> output, '<td align=\"left\"><A HREF=\"#\" onClick=\"popup(\'%s\',\'%s\')\">Info</A></td>' % (str(updated_tag), str(html_body_text))
- else:
- print >> output, '<td align=\"left\"></td>'
- # print execution time
- print >> output, '<td align="left"><A HREF=\"%s\">Debug</A></td>' % os.path.join(dirname, res['title'], "debug")
+ html_body_text = '<html><head><title>%s</title></head><body>%s</body></html>' % (str(updated_tag), log_text)
+ print >> output, '<td align=\"left\"><A HREF=\"#\" onClick=\"popup(\'%s\',\'%s\')\">Info</A></td>' % (str(updated_tag), str(html_body_text))
+ else:
+ print >> output, '<td align=\"left\"></td>'
+ # print execution time
+ print >> output, '<td align="left"><A HREF=\"%s\">Debug</A></td>' % os.path.join(dirname, res['subdir'], "debug")
- print >> output, '</tr>'
+ print >> output, '</tr>'
+ print_result(results[r][1], indent + 1)
+
+ print_result(results[""][1], 0)
print >> output, "</tbody></table>"
@@ -1528,15 +1539,27 @@
output.close()
-def parse_result(dirname, line):
+def parse_result(dirname, line, results_data):
+ """
+ Parse job status log line.
+
+ @param dirname: Job results dir
+ @param line: Status log line.
+ @param results_data: Dictionary with for results.
+ """
parts = line.split()
if len(parts) < 4:
return None
- global stimelist
+ global tests
if parts[0] == 'START':
pair = parts[3].split('=')
stime = int(pair[1])
- stimelist.append(stime)
+ results_data[parts[1]] = [stime, [], None]
+ try:
+ parent_test = re.findall(r".*/", parts[1])[0][:-1]
+ results_data[parent_test][1].append(parts[1])
+ except IndexError:
+ results_data[""][1].append(parts[1])
elif (parts[0] == 'END'):
result = {}
@@ -1553,32 +1576,39 @@
result['exec_time_sec'] = 'na'
tag = parts[3]
+ result['subdir'] = parts[2]
# assign actual values
rx = re.compile('^(\w+)\.(.*)$')
m1 = rx.findall(parts[3])
- result['testcase'] = m1[0][1]
+ if len(m1):
+ result['testcase'] = m1[0][1]
+ else:
+ result['testcase'] = parts[3]
result['title'] = str(tag)
result['status'] = parts[1]
if result['status'] != 'GOOD':
result['log'] = get_exec_log(dirname, tag)
- if len(stimelist)>0:
+ if len(results_data)>0:
pair = parts[4].split('=')
- try:
- etime = int(pair[1])
- stime = stimelist.pop()
- total_exec_time_sec = etime - stime
- result['exec_time_sec'] = total_exec_time_sec
- except ValueError:
- result['exec_time_sec'] = "Unknown"
- return result
+ etime = int(pair[1])
+ stime = results_data[parts[2]][0]
+ total_exec_time_sec = etime - stime
+ result['exec_time_sec'] = total_exec_time_sec
+ results_data[parts[2]][2] = result
return None
def get_exec_log(resdir, tag):
- stdout_file = os.path.join(resdir, tag) + '/debug/stdout'
- stderr_file = os.path.join(resdir, tag) + '/debug/stderr'
- status_file = os.path.join(resdir, tag) + '/status'
- dmesg_file = os.path.join(resdir, tag) + '/sysinfo/dmesg'
+ """
+ Get job execution summary.
+
+ @param resdir: Job results dir.
+ @param tag: Job tag.
+ """
+ stdout_file = os.path.join(resdir, tag, 'debug', 'stdout')
+ stderr_file = os.path.join(resdir, tag, 'debug', 'stderr')
+ status_file = os.path.join(resdir, tag, 'status')
+ dmesg_file = os.path.join(resdir, tag, 'sysinfo', 'dmesg')
log = ''
log += '<br><b>STDERR:</b><br>'
log += get_info_file(stderr_file)
@@ -1592,6 +1622,13 @@
def get_info_file(filename):
+ """
+ Gets the contents of an autotest info file.
+
+ It also and highlights the file contents with possible problems.
+
+ @param filename: Info file path.
+ """
data = ''
errors = re.compile(r"\b(error|fail|failed)\b", re.IGNORECASE)
if os.path.isfile(filename):
@@ -1613,8 +1650,10 @@
return data
-
def usage():
+ """
+ Print stand alone program usage.
+ """
print 'usage:',
print 'make_html_report.py -r <result_directory> [-f output_file] [-R]'
print '(e.g. make_html_reporter.py -r '\
@@ -1629,6 +1668,9 @@
"""
Return the value of the first appearance of key in any keyval file in
result_dir. If no appropriate line is found, return 'Unknown'.
+
+ @param result_dir: Path that holds the keyval files.
+ @param key: Specific key we're retrieving.
"""
keyval_pattern = os.path.join(result_dir, "kvm.*", "keyval")
keyval_lines = commands.getoutput(r"grep -h '\b%s\b.*=' %s"
@@ -1646,15 +1688,68 @@
"""
Return an HTML string describing the KVM version.
- @param result_dir: An Autotest job result dir
+ @param result_dir: An Autotest job result dir.
"""
kvm_version = get_keyval_value(result_dir, "kvm_version")
kvm_userspace_version = get_keyval_value(result_dir,
"kvm_userspace_version")
+ if kvm_version == "Unknown" or kvm_userspace_version == "Unknown":
+ return None
return "Kernel: %s<br>Userspace: %s" % (kvm_version, kvm_userspace_version)
+def create_report(dirname, html_path='', output_file_name=None):
+ """
+ Create an HTML report with info about an autotest client job.
+
+ If no relative path (html_path) or output file name provided, an HTML
+ file in the toplevel job results dir called 'job_report.html' will be
+ created, with relative links.
+
+ @param html_path: Prefix for the HTML links. Useful to specify absolute
+ in the report (not wanted most of the time).
+ @param output_file_name: Path to the report file.
+ """
+ res_dir = os.path.abspath(dirname)
+ tag = res_dir
+ status_file_name = os.path.join(dirname, 'status')
+ sysinfo_dir = os.path.join(dirname, 'sysinfo')
+ host = get_info_file(os.path.join(sysinfo_dir, 'hostname'))
+ rx = re.compile('^\s+[END|START].*$')
+ # create the results set dict
+ results_data = {}
+ results_data[""] = [0, [], None]
+ if os.path.exists(status_file_name):
+ f = open(status_file_name, "r")
+ lines = f.readlines()
+ f.close()
+ for line in lines:
+ if rx.match(line):
+ parse_result(dirname, line, results_data)
+ # create the meta info dict
+ metalist = {
+ 'uname': get_info_file(os.path.join(sysinfo_dir, 'uname')),
+ 'cpuinfo':get_info_file(os.path.join(sysinfo_dir, 'cpuinfo')),
+ 'meminfo':get_info_file(os.path.join(sysinfo_dir, 'meminfo')),
+ 'df':get_info_file(os.path.join(sysinfo_dir, 'df')),
+ 'modules':get_info_file(os.path.join(sysinfo_dir, 'modules')),
+ 'gcc':get_info_file(os.path.join(sysinfo_dir, 'gcc_--version')),
+ 'dmidecode':get_info_file(os.path.join(sysinfo_dir, 'dmidecode')),
+ 'dmesg':get_info_file(os.path.join(sysinfo_dir, 'dmesg')),
+ }
+ if get_kvm_version(dirname) is not None:
+ metalist['kvm_ver'] = get_kvm_version(dirname)
+
+ if output_file_name is None:
+ output_file_name = os.path.join(dirname, 'job_report.html')
+ make_html_file(metalist, results_data, tag, host, output_file_name,
+ html_path)
+
+
def main(argv):
+ """
+ Parses the arguments and executes the stand alone program.
+ """
dirname = None
output_file_name = None
relative_path = False
@@ -1685,38 +1780,7 @@
if dirname:
if os.path.isdir(dirname): # TBD: replace it with a validation of
# autotest result dir
- res_dir = os.path.abspath(dirname)
- tag = res_dir
- status_file_name = dirname + '/status'
- sysinfo_dir = dirname + '/sysinfo'
- host = get_info_file('%s/hostname' % sysinfo_dir)
- rx = re.compile('^\s+[END|START].*$')
- # create the results set dict
- results_data = []
- if os.path.exists(status_file_name):
- f = open(status_file_name, "r")
- lines = f.readlines()
- f.close()
- for line in lines:
- if rx.match(line):
- result_dict = parse_result(dirname, line)
- if result_dict:
- results_data.append(result_dict)
- # create the meta info dict
- metalist = {
- 'uname': get_info_file('%s/uname' % sysinfo_dir),
- 'cpuinfo':get_info_file('%s/cpuinfo' % sysinfo_dir),
- 'meminfo':get_info_file('%s/meminfo' % sysinfo_dir),
- 'df':get_info_file('%s/df' % sysinfo_dir),
- 'modules':get_info_file('%s/modules' % sysinfo_dir),
- 'gcc':get_info_file('%s/gcc_--version' % sysinfo_dir),
- 'dmidecode':get_info_file('%s/dmidecode' % sysinfo_dir),
- 'dmesg':get_info_file('%s/dmesg' % sysinfo_dir),
- 'kvmver':get_kvm_version(dirname)
- }
-
- make_html_file(metalist, results_data, tag, host, output_file_name,
- html_path)
+ create_report(dirname, html_path, output_file_name)
sys.exit(0)
else:
print 'Invalid result directory <%s>' % dirname