OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 """ | 2 """ |
3 Script used to parse the test results and generate an HTML report. | 3 Script used to parse the test results and generate an HTML report. |
4 | 4 |
5 @copyright: (c)2005-2007 Matt Kruse (javascripttoolbox.com) | 5 @copyright: (c)2005-2007 Matt Kruse (javascripttoolbox.com) |
6 @copyright: Red Hat 2008-2009 | 6 @copyright: Red Hat 2008-2009 |
7 @author: Dror Russo (drusso@redhat.com) | 7 @author: Dror Russo (drusso@redhat.com) |
8 """ | 8 """ |
9 | 9 |
10 import os, sys, re, getopt, time, datetime, commands | 10 import os, sys, re, getopt, time, datetime, commands |
11 import common | 11 import common |
12 | 12 |
13 | 13 |
14 format_css=""" | 14 format_css = """ |
15 html,body { | 15 html,body { |
16 padding:0; | 16 padding:0; |
17 color:#222; | 17 color:#222; |
18 background:#FFFFFF; | 18 background:#FFFFFF; |
19 } | 19 } |
20 | 20 |
21 body { | 21 body { |
22 padding:0px; | 22 padding:0px; |
23 font:76%/150% "Lucida Grande", "Lucida Sans Unicode", Lucida, Verdana, Genev
a, Arial, Helvetica, sans-serif; | 23 font:76%/150% "Lucida Grande", "Lucida Sans Unicode", Lucida, Verdana, Genev
a, Arial, Helvetica, sans-serif; |
24 } | 24 } |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
173 | 173 |
174 /* Format menu items differently depending on what level of the tree the
y are in */ | 174 /* Format menu items differently depending on what level of the tree the
y are in */ |
175 /* Uncomment this if you want your fonts to decrease in size the deeper
they are in the tree */ | 175 /* Uncomment this if you want your fonts to decrease in size the deeper
they are in the tree */ |
176 /* | 176 /* |
177 ul.mktree li ul li { font-size: 90% } | 177 ul.mktree li ul li { font-size: 90% } |
178 */ | 178 */ |
179 } | 179 } |
180 """ | 180 """ |
181 | 181 |
182 | 182 |
183 table_js=""" | 183 table_js = """ |
184 /** | 184 /** |
185 * Copyright (c)2005-2007 Matt Kruse (javascripttoolbox.com) | 185 * Copyright (c)2005-2007 Matt Kruse (javascripttoolbox.com) |
186 * | 186 * |
187 * Dual licensed under the MIT and GPL licenses. | 187 * Dual licensed under the MIT and GPL licenses. |
188 * This basically means you can use this code however you want for | 188 * This basically means you can use this code however you want for |
189 * free, but don't claim to have written it yourself! | 189 * free, but don't claim to have written it yourself! |
190 * Donations always accepted: http://www.JavascriptToolbox.com/donate/ | 190 * Donations always accepted: http://www.JavascriptToolbox.com/donate/ |
191 * | 191 * |
192 * Please do not link to the .js files on javascripttoolbox.com from | 192 * Please do not link to the .js files on javascripttoolbox.com from |
193 * your site. Copy the files locally to your server instead. | 193 * your site. Copy the files locally to your server instead. |
(...skipping 1179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1373 } | 1373 } |
1374 } | 1374 } |
1375 """ | 1375 """ |
1376 | 1376 |
1377 | 1377 |
1378 ################################################################# | 1378 ################################################################# |
1379 ## This script gets kvm autotest results directory path as an ## | 1379 ## This script gets kvm autotest results directory path as an ## |
1380 ## input and create a single html formatted result page. ## | 1380 ## input and create a single html formatted result page. ## |
1381 ################################################################# | 1381 ################################################################# |
1382 | 1382 |
1383 stimelist=[] | 1383 stimelist = [] |
1384 | 1384 |
1385 | 1385 |
1386 def make_html_file(metadata, results, tag, host, output_file_name, dirname): | 1386 def make_html_file(metadata, results, tag, host, output_file_name, dirname): |
1387 html_prefix=""" | 1387 html_prefix = """ |
1388 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/st
rict.dtd"> | 1388 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/st
rict.dtd"> |
1389 <html> | 1389 <html> |
1390 <head> | 1390 <head> |
1391 <title>KVM Autotest Results</title> | 1391 <title>KVM Autotest Results</title> |
1392 <style type="text/css"> | 1392 <style type="text/css"> |
1393 %s | 1393 %s |
1394 </style> | 1394 </style> |
1395 <script type="text/javascript"> | 1395 <script type="text/javascript"> |
1396 %s | 1396 %s |
1397 %s | 1397 %s |
1398 function popup(tag,text) { | 1398 function popup(tag,text) { |
1399 var w = window.open('', tag, 'toolbar=no,location=no,directories=no,status=no,me
nubar=no,scrollbars=yes,resizable=yes, copyhistory=no,width=600,height=300,top=2
0,left=100'); | 1399 var w = window.open('', tag, 'toolbar=no,location=no,directories=no,status=no,me
nubar=no,scrollbars=yes,resizable=yes, copyhistory=no,width=600,height=300,top=2
0,left=100'); |
1400 w.document.open("text/html", "replace"); | 1400 w.document.open("text/html", "replace"); |
1401 w.document.write(text); | 1401 w.document.write(text); |
1402 w.document.close(); | 1402 w.document.close(); |
1403 return true; | 1403 return true; |
1404 } | 1404 } |
1405 </script> | 1405 </script> |
1406 </head> | 1406 </head> |
1407 <body> | 1407 <body> |
1408 """%(format_css, table_js, maketree_js) | 1408 """ % (format_css, table_js, maketree_js) |
1409 | 1409 |
1410 | 1410 |
1411 if output_file_name: | 1411 if output_file_name: |
1412 output = open(output_file_name, "w") | 1412 output = open(output_file_name, "w") |
1413 else: #if no output file defined, print html file to console | 1413 else: #if no output file defined, print html file to console |
1414 output = sys.stdout | 1414 output = sys.stdout |
1415 # create html page | 1415 # create html page |
1416 print >> output, html_prefix | 1416 print >> output, html_prefix |
1417 print >> output, '<h2 id=\"page_title\">KVM Autotest Execution Report</h2>' | 1417 print >> output, '<h2 id=\"page_title\">KVM Autotest Execution Report</h2>' |
1418 | 1418 |
1419 # formating date and time to print | 1419 # formating date and time to print |
1420 t = datetime.datetime.now() | 1420 t = datetime.datetime.now() |
1421 | 1421 |
1422 epoch_sec = time.mktime(t.timetuple()) | 1422 epoch_sec = time.mktime(t.timetuple()) |
1423 now = datetime.datetime.fromtimestamp(epoch_sec) | 1423 now = datetime.datetime.fromtimestamp(epoch_sec) |
1424 | 1424 |
1425 # basic statistics | 1425 # basic statistics |
1426 total_executed = 0 | 1426 total_executed = 0 |
1427 total_failed = 0 | 1427 total_failed = 0 |
1428 total_passed = 0 | 1428 total_passed = 0 |
1429 for res in results: | 1429 for res in results: |
1430 total_executed+=1 | 1430 total_executed += 1 |
1431 if res['status'] == 'GOOD': | 1431 if res['status'] == 'GOOD': |
1432 total_passed+=1 | 1432 total_passed += 1 |
1433 else: | 1433 else: |
1434 total_failed+=1 | 1434 total_failed += 1 |
1435 stat_str = 'No test cases executed' | 1435 stat_str = 'No test cases executed' |
1436 if total_executed>0: | 1436 if total_executed > 0: |
1437 failed_perct = int(float(total_failed)/float(total_executed)*100) | 1437 failed_perct = int(float(total_failed)/float(total_executed)*100) |
1438 stat_str = ('From %d tests executed, %d have passed (%d%% failures)' % | 1438 stat_str = ('From %d tests executed, %d have passed (%d%% failures)' % |
1439 (total_executed, total_passed, failed_perct)) | 1439 (total_executed, total_passed, failed_perct)) |
1440 | 1440 |
1441 kvm_ver_str = metadata['kvmver'] | 1441 kvm_ver_str = metadata['kvmver'] |
1442 | 1442 |
1443 print >> output, '<table class="stats2">' | 1443 print >> output, '<table class="stats2">' |
1444 print >> output, '<tr><td>HOST</td><td>:</td><td>%s</td></tr>' % host | 1444 print >> output, '<tr><td>HOST</td><td>:</td><td>%s</td></tr>' % host |
1445 print >> output, '<tr><td>RESULTS DIR</td><td>:</td><td>%s</td></tr>' % tag | 1445 print >> output, '<tr><td>RESULTS DIR</td><td>:</td><td>%s</td></tr>' % tag |
1446 print >> output, '<tr><td>DATE</td><td>:</td><td>%s</td></tr>' % now.ctime() | 1446 print >> output, '<tr><td>DATE</td><td>:</td><td>%s</td></tr>' % now.ctime() |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1479 elif res['status'] == 'ERROR': | 1479 elif res['status'] == 'ERROR': |
1480 print >> output, '<td align=\"left\"><b><font color="red">ERROR!</fo
nt></b></td>' | 1480 print >> output, '<td align=\"left\"><b><font color="red">ERROR!</fo
nt></b></td>' |
1481 else: | 1481 else: |
1482 print >> output, '<td align=\"left\">%s</td>' % res['status'] | 1482 print >> output, '<td align=\"left\">%s</td>' % res['status'] |
1483 # print exec time (seconds) | 1483 # print exec time (seconds) |
1484 print >> output, '<td align="left">%s</td>' % res['exec_time_sec'] | 1484 print >> output, '<td align="left">%s</td>' % res['exec_time_sec'] |
1485 # print log only if test failed.. | 1485 # print log only if test failed.. |
1486 if res['log']: | 1486 if res['log']: |
1487 #chop all '\n' from log text (to prevent html errors) | 1487 #chop all '\n' from log text (to prevent html errors) |
1488 rx1 = re.compile('(\s+)') | 1488 rx1 = re.compile('(\s+)') |
1489 log_text = rx1.sub(' ',res['log']) | 1489 log_text = rx1.sub(' ', res['log']) |
1490 | 1490 |
1491 # allow only a-zA-Z0-9_ in html title name | 1491 # allow only a-zA-Z0-9_ in html title name |
1492 # (due to bug in MS-explorer) | 1492 # (due to bug in MS-explorer) |
1493 rx2 = re.compile('([^a-zA-Z_0-9])') | 1493 rx2 = re.compile('([^a-zA-Z_0-9])') |
1494 updated_tag = rx2.sub('_',res['title']) | 1494 updated_tag = rx2.sub('_', res['title']) |
1495 | 1495 |
1496 html_body_text = '<html><head><title>%s</title></head><body>%s</body
></html>'%(str(updated_tag),log_text) | 1496 html_body_text = '<html><head><title>%s</title></head><body>%s</body
></html>' % (str(updated_tag), log_text) |
1497 print >> output, '<td align=\"left\"><A HREF=\"#\" onClick=\"popup(\
'%s\',\'%s\')\">Info</A></td>'%(str(updated_tag),str(html_body_text)) | 1497 print >> output, '<td align=\"left\"><A HREF=\"#\" onClick=\"popup(\
'%s\',\'%s\')\">Info</A></td>' % (str(updated_tag), str(html_body_text)) |
1498 else: | 1498 else: |
1499 print >> output, '<td align=\"left\"></td>' | 1499 print >> output, '<td align=\"left\"></td>' |
1500 # print execution time | 1500 # print execution time |
1501 print >> output, '<td align="left"><A HREF=\"%s\">Debug</A></td>' % os.p
ath.join(dirname, res['title'], "debug") | 1501 print >> output, '<td align="left"><A HREF=\"%s\">Debug</A></td>' % os.p
ath.join(dirname, res['title'], "debug") |
1502 | 1502 |
1503 print >> output, '</tr>' | 1503 print >> output, '</tr>' |
1504 print >> output, "</tbody></table>" | 1504 print >> output, "</tbody></table>" |
1505 | 1505 |
1506 | 1506 |
1507 print >> output, '<h2 id=\"page_sub_title\">Host Info</h2>' | 1507 print >> output, '<h2 id=\"page_sub_title\">Host Info</h2>' |
1508 print >> output, '<h2 id=\"comment\">click on each item to expend/collapse</
h2>' | 1508 print >> output, '<h2 id=\"comment\">click on each item to expend/collapse</
h2>' |
1509 ## Meta list comes here.. | 1509 ## Meta list comes here.. |
1510 print >> output, '<p>' | 1510 print >> output, '<p>' |
1511 print >> output, '<A href="#" class="button" onClick="expandTree(\'meta_tree
\');return false;">Expand All</A>' | 1511 print >> output, '<A href="#" class="button" onClick="expandTree(\'meta_tree
\');return false;">Expand All</A>' |
1512 print >> output, '  ' | 1512 print >> output, '  ' |
1513 print >> output, '<A class="button" href="#" onClick="collapseTree(\'meta_tr
ee\'); return false;">Collapse All</A>' | 1513 print >> output, '<A class="button" href="#" onClick="collapseTree(\'meta_tr
ee\'); return false;">Collapse All</A>' |
1514 print >> output, '</p>' | 1514 print >> output, '</p>' |
1515 | 1515 |
1516 print >> output, '<ul class="mktree" id="meta_tree">' | 1516 print >> output, '<ul class="mktree" id="meta_tree">' |
1517 counter=0 | 1517 counter = 0 |
1518 keys = metadata.keys() | 1518 keys = metadata.keys() |
1519 keys.sort() | 1519 keys.sort() |
1520 for key in keys: | 1520 for key in keys: |
1521 val = metadata[key] | 1521 val = metadata[key] |
1522 print >> output, '<li id=\"meta_headline\">%s' % key | 1522 print >> output, '<li id=\"meta_headline\">%s' % key |
1523 print >> output, '<ul><table class="meta_table"><tr><td align="left">%s<
/td></tr></table></ul></li>' % val | 1523 print >> output, '<ul><table class="meta_table"><tr><td align="left">%s<
/td></tr></table></ul></li>' % val |
1524 print >> output, '</ul>' | 1524 print >> output, '</ul>' |
1525 | 1525 |
1526 print >> output, "</body></html>" | 1526 print >> output, "</body></html>" |
1527 if output_file_name: | 1527 if output_file_name: |
1528 output.close() | 1528 output.close() |
1529 | 1529 |
1530 | 1530 |
1531 def parse_result(dirname,line): | 1531 def parse_result(dirname, line): |
1532 parts = line.split() | 1532 parts = line.split() |
1533 if len(parts) < 4: | 1533 if len(parts) < 4: |
1534 return None | 1534 return None |
1535 global stimelist | 1535 global stimelist |
1536 if parts[0] == 'START': | 1536 if parts[0] == 'START': |
1537 pair = parts[3].split('=') | 1537 pair = parts[3].split('=') |
1538 stime = int(pair[1]) | 1538 stime = int(pair[1]) |
1539 stimelist.append(stime) | 1539 stimelist.append(stime) |
1540 | 1540 |
1541 elif (parts[0] == 'END'): | 1541 elif (parts[0] == 'END'): |
(...skipping 11 matching lines...) Expand all Loading... |
1553 result['exec_time_sec'] = 'na' | 1553 result['exec_time_sec'] = 'na' |
1554 tag = parts[3] | 1554 tag = parts[3] |
1555 | 1555 |
1556 # assign actual values | 1556 # assign actual values |
1557 rx = re.compile('^(\w+)\.(.*)$') | 1557 rx = re.compile('^(\w+)\.(.*)$') |
1558 m1 = rx.findall(parts[3]) | 1558 m1 = rx.findall(parts[3]) |
1559 result['testcase'] = m1[0][1] | 1559 result['testcase'] = m1[0][1] |
1560 result['title'] = str(tag) | 1560 result['title'] = str(tag) |
1561 result['status'] = parts[1] | 1561 result['status'] = parts[1] |
1562 if result['status'] != 'GOOD': | 1562 if result['status'] != 'GOOD': |
1563 result['log'] = get_exec_log(dirname,tag) | 1563 result['log'] = get_exec_log(dirname, tag) |
1564 if len(stimelist)>0: | 1564 if len(stimelist)>0: |
1565 pair = parts[4].split('=') | 1565 pair = parts[4].split('=') |
1566 etime = int(pair[1]) | 1566 etime = int(pair[1]) |
1567 stime = stimelist.pop() | 1567 stime = stimelist.pop() |
1568 total_exec_time_sec = etime - stime | 1568 total_exec_time_sec = etime - stime |
1569 result['exec_time_sec'] = total_exec_time_sec | 1569 result['exec_time_sec'] = total_exec_time_sec |
1570 return result | 1570 return result |
1571 return None | 1571 return None |
1572 | 1572 |
1573 | 1573 |
1574 def get_exec_log(resdir, tag): | 1574 def get_exec_log(resdir, tag): |
1575 stdout_file = os.path.join(resdir,tag) + '/debug/stdout' | 1575 stdout_file = os.path.join(resdir, tag) + '/debug/stdout' |
1576 stderr_file = os.path.join(resdir,tag) + '/debug/stderr' | 1576 stderr_file = os.path.join(resdir, tag) + '/debug/stderr' |
1577 status_file = os.path.join(resdir,tag) + '/status' | 1577 status_file = os.path.join(resdir, tag) + '/status' |
1578 dmesg_file = os.path.join(resdir,tag) + '/sysinfo/dmesg' | 1578 dmesg_file = os.path.join(resdir, tag) + '/sysinfo/dmesg' |
1579 log = '' | 1579 log = '' |
1580 log += '<br><b>STDERR:</b><br>' | 1580 log += '<br><b>STDERR:</b><br>' |
1581 log += get_info_file(stderr_file) | 1581 log += get_info_file(stderr_file) |
1582 log += '<br><b>STDOUT:</b><br>' | 1582 log += '<br><b>STDOUT:</b><br>' |
1583 log += get_info_file(stdout_file) | 1583 log += get_info_file(stdout_file) |
1584 log += '<br><b>STATUS:</b><br>' | 1584 log += '<br><b>STATUS:</b><br>' |
1585 log += get_info_file(status_file) | 1585 log += get_info_file(status_file) |
1586 log += '<br><b>DMESG:</b><br>' | 1586 log += '<br><b>DMESG:</b><br>' |
1587 log += get_info_file(dmesg_file) | 1587 log += get_info_file(dmesg_file) |
1588 return log | 1588 return log |
1589 | 1589 |
1590 | 1590 |
1591 def get_info_file(filename): | 1591 def get_info_file(filename): |
1592 data='' | 1592 data = '' |
1593 errors = re.compile(r"\b(error|fail|failed)\b", re.IGNORECASE) | 1593 errors = re.compile(r"\b(error|fail|failed)\b", re.IGNORECASE) |
1594 if os.path.isfile(filename): | 1594 if os.path.isfile(filename): |
1595 f = open('%s' % filename, "r") | 1595 f = open('%s' % filename, "r") |
1596 lines=f.readlines() | 1596 lines = f.readlines() |
1597 f.close() | 1597 f.close() |
1598 rx = re.compile('(\'|\")') | 1598 rx = re.compile('(\'|\")') |
1599 for line in lines: | 1599 for line in lines: |
1600 new_line = rx.sub('',line) | 1600 new_line = rx.sub('', line) |
1601 errors_found = errors.findall(new_line) | 1601 errors_found = errors.findall(new_line) |
1602 if len(errors_found)>0: | 1602 if len(errors_found) > 0: |
1603 data += '<font color=red>%s</font><br>'%str(new_line) | 1603 data += '<font color=red>%s</font><br>' % str(new_line) |
1604 else: | 1604 else: |
1605 data += '%s<br>'%str(new_line) | 1605 data += '%s<br>' % str(new_line) |
1606 if not data: | 1606 if not data: |
1607 data = 'No Information Found.<br>' | 1607 data = 'No Information Found.<br>' |
1608 else: | 1608 else: |
1609 data = 'File not found.<br>' | 1609 data = 'File not found.<br>' |
1610 return data | 1610 return data |
1611 | 1611 |
1612 | 1612 |
1613 | 1613 |
1614 def usage(): | 1614 def usage(): |
1615 print 'usage:', | 1615 print 'usage:', |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1680 html_path = '' | 1680 html_path = '' |
1681 | 1681 |
1682 if dirname: | 1682 if dirname: |
1683 if os.path.isdir(dirname): # TBD: replace it with a validation of | 1683 if os.path.isdir(dirname): # TBD: replace it with a validation of |
1684 # autotest result dir | 1684 # autotest result dir |
1685 res_dir = os.path.abspath(dirname) | 1685 res_dir = os.path.abspath(dirname) |
1686 tag = res_dir | 1686 tag = res_dir |
1687 status_file_name = dirname + '/status' | 1687 status_file_name = dirname + '/status' |
1688 sysinfo_dir = dirname + '/sysinfo' | 1688 sysinfo_dir = dirname + '/sysinfo' |
1689 host = get_info_file('%s/hostname' % sysinfo_dir) | 1689 host = get_info_file('%s/hostname' % sysinfo_dir) |
1690 rx=re.compile('^\s+[END|START].*$') | 1690 rx = re.compile('^\s+[END|START].*$') |
1691 # create the results set dict | 1691 # create the results set dict |
1692 results_data=[] | 1692 results_data = [] |
1693 if os.path.exists(status_file_name): | 1693 if os.path.exists(status_file_name): |
1694 f = open(status_file_name, "r") | 1694 f = open(status_file_name, "r") |
1695 lines=f.readlines() | 1695 lines = f.readlines() |
1696 f.close() | 1696 f.close() |
1697 for line in lines: | 1697 for line in lines: |
1698 if rx.match(line): | 1698 if rx.match(line): |
1699 result_dict = parse_result(dirname, line) | 1699 result_dict = parse_result(dirname, line) |
1700 if result_dict: | 1700 if result_dict: |
1701 results_data.append(result_dict) | 1701 results_data.append(result_dict) |
1702 # create the meta info dict | 1702 # create the meta info dict |
1703 metalist = { | 1703 metalist = { |
1704 'uname': get_info_file('%s/uname' % sysinfo_dir), | 1704 'uname': get_info_file('%s/uname' % sysinfo_dir), |
1705 'cpuinfo':get_info_file('%s/cpuinfo' % sysinfo_dir), | 1705 'cpuinfo':get_info_file('%s/cpuinfo' % sysinfo_dir), |
(...skipping 12 matching lines...) Expand all Loading... |
1718 else: | 1718 else: |
1719 print 'Invalid result directory <%s>' % dirname | 1719 print 'Invalid result directory <%s>' % dirname |
1720 sys.exit(1) | 1720 sys.exit(1) |
1721 else: | 1721 else: |
1722 usage() | 1722 usage() |
1723 sys.exit(1) | 1723 sys.exit(1) |
1724 | 1724 |
1725 | 1725 |
1726 if __name__ == "__main__": | 1726 if __name__ == "__main__": |
1727 main(sys.argv[1:]) | 1727 main(sys.argv[1:]) |
OLD | NEW |