X-Git-Url: https://gerrit.fd.io/r/gitweb?p=csit.git;a=blobdiff_plain;f=resources%2Ftools%2Frobot_output_parser.py;h=fb085980f6f716f58cd3bc2059c48d42f84d7e5f;hp=28a9f268ef62b75130503ec5ef5bc3ae07f15a15;hb=7af34879d4c2a0c0994d93ded5d9a686c860e8da;hpb=a881153db68401e040f37262d60b5d5e3cc486ac diff --git a/resources/tools/robot_output_parser.py b/resources/tools/robot_output_parser.py index 28a9f268ef..fb085980f6 100755 --- a/resources/tools/robot_output_parser.py +++ b/resources/tools/robot_output_parser.py @@ -14,97 +14,121 @@ # limitations under the License. """Script parses the data taken by robot framework (output.xml) and dumps -intereted values into JSON output file.""" +intereted values into XML output file.""" -import json +import argparse import re -import sys, getopt +import sys +import xml.etree.ElementTree as ET from robot.api import ExecutionResult, ResultVisitor -class ExecutionTestChecker(ResultVisitor): +class ExecutionChecker(ResultVisitor): """Iterates through test cases.""" - def __init__(self, vDeviceVersion): - self.vDeviceVersion = vDeviceVersion - self.out = [] + tc_regexp = re.compile(ur'^TC\d+:\s((\d+)B|IMIX_v4_1)[\D\d]+\s(\d)'\ + '(thread|threads)\\s(\\d)(core|cores)\\s(\\d)(rxq|rxqs)') + rate_regexp = re.compile(ur'^[\D\d]*FINAL_RATE:\s(\d+\.\d+)[\D\d]*') + lat_regexp = re.compile(ur'^[\D\d]*'\ + 'LAT_\\d+%NDR:\\s\\[\'(\\d+\\/\\d+\\/\\d+)\',\\s\'(\\d+\\/\\d+\\/\\d+)\'\\]\\s\n'\ + 'LAT_\\d+%NDR:\\s\\[\'(\\d+\\/\\d+\\/\\d+)\',\\s\'(\\d+\\/\\d+\\/\\d+)\'\\]\\s\n'\ + 'LAT_\\d+%NDR:\\s\\[\'(\\d+\\/\\d+\\/\\d+)\',\\s\'(\\d+\\/\\d+\\/\\d+)\'\\]') + + def __init__(self, args): + self.root = ET.Element('build', + attrib={'vdevice': args.vdevice}) + + def visit_suite(self, suite): + """Implements traversing through the suite and its direct children. + + :param suite: Suite to process. + :type suite: Suite + :return: Nothing. + """ + if self.start_suite(suite) is not False: + suite.suites.visit(self) + suite.tests.visit(self) + self.end_suite(suite) + + def start_suite(self, suite): + """Called when suite starts. + + :param suite: Suite to process. + :type suite: Suite + :return: Nothing. + """ + pass + + def end_suite(self, suite): + """Called when suite ends. + + :param suite: Suite to process. + :type suite: Suite + :return: Nothing. + """ + pass def visit_test(self, test): - """Overloaded function. Called when test is found to process data. + """Implements traversing through the test. :param test: Test to process. - :type test: ExecutionTestChecker + :type test: Test + :return: Nothing. """ + if self.start_test(test) is not False: + self.end_test(test) - test_id = test.longname - test_status = 'Failed' - framesize = '' - throughput = '' - throughput_units = '' - workers_per_nic = '' - workers = '' + def start_test(self, test): + """Called when test starts. - if any("PERFTEST" in tag for tag in test.tags): + :param test: Test to process. + :type test: Test + :return: Nothing. + """ + if any("PERFTEST_LONG" in tag for tag in test.tags): if test.status == 'PASS': - test_status = 'Passed' - if any("PERFTEST_LONG" in tag for tag in test.tags): - throughput = test.message.split(' ')[1] - throughput_units = test.message.split(' ')[2] - elif any("PERFTEST_SHORT" in tag for tag in test.tags): - for keyword in test.keywords: - for assign in keyword.assign: - if assign == '${rate}': - temp = re.findall(r"(\d*\.\d+|\d+)([A-Za-z]*)", - keyword.args[0]) - throughput = temp[0][0] - throughput_units = temp[0][1] - - for keyword in test.keywords: - for assign in keyword.assign: - if assign == '${framesize}': - framesize = keyword.args[0] - if 'worker threads' in keyword.name: - workers = keyword.name.split('\'')[1] - workers_per_nic = keyword.name.split('\'')[3] - - self.out.append({'testCase': { - 'testId': test_id, - 'testStatus': test_status, - 'workerThreads': workers, - 'workerThreadsPerNic': workers_per_nic, - 'testTags': [tag for tag in test.tags], - 'l2FrameSize': {'value': framesize, - 'units': 'Bytes'}, - 'throughput': {'value': throughput, - 'units': throughput_units}, - 'vDevice': {'version': self.vDeviceVersion}}}) - - -def parse_tests(xml_file, vDeviceVersion): - """Parser result of robot output file and return. - - :param xml_file: Output.xml from robot run. - :param vDeviceVersion: vDevice version. - :type xml_file: file - :type vDeviceVersion: str - - :return: JSON formatted output. - :rtype: dict - """ + tags = [] + for tag in test.tags: + tags.append(tag) + test_elem = ET.SubElement(self.root, + test.parent.name.replace(" ", "")) + test_elem.attrib['name'] = test.parent.name + test_elem.attrib['framesize'] = str(re.search(\ + self.tc_regexp, test.name).group(2)) + test_elem.attrib['workerthreads'] = str(re.search(\ + self.tc_regexp, test.name).group(3)) + test_elem.attrib['workerspernic'] = str(re.search(\ + self.tc_regexp, test.name).group(7)) + test_elem.attrib['tags'] = ', '.join(tags) + test_elem.text = str(re.search(\ + self.rate_regexp, test.message).group(1)) + + def end_test(self, test): + """Called when test ends. - result = ExecutionResult(xml_file) - checker = ExecutionTestChecker(vDeviceVersion) - result.visit(checker) + :param test: Test to process. + :type test: Test + :return: Nothing. + """ + pass - return checker.out +def parse_tests(args): + """Process data from robot output.xml file and return XML data. -def print_help(): - """Print help on stdout.""" + :param args: Parsed arguments. + :type args: ArgumentParser - print "args: [-h] -i -o " + \ - " -v " + :return: XML formatted output. + :rtype: ElementTree + """ + + result = ExecutionResult(args.input) + checker = ExecutionChecker(args) + result.visit(checker) + + return checker.root def print_error(msg): @@ -118,44 +142,34 @@ def print_error(msg): sys.stderr.write(msg+'\n') -def main(argv): +def parse_args(): + """Parse arguments from cmd line. + + :return: Parsed arguments. + :rtype ArgumentParser + """ + + parser = argparse.ArgumentParser() + parser.add_argument("-i", "--input", required=True, + type=argparse.FileType('r'), + help="Robot XML log file") + parser.add_argument("-o", "--output", required=True, + type=argparse.FileType('w'), + help="XML output file") + parser.add_argument("-v", "--vdevice", required=True, + help="VPP version") + + return parser.parse_args() + + +def main(): """Main function.""" - _log_file = None - _json_file = None - _vpp = None - - try: - opts, _ = getopt.getopt(argv, "hi:o:v:", ["help"]) - except getopt.GetoptError: - print_help() - sys.exit(1) - - for opt, arg in opts: - if opt in ('-h', "--help"): - print_help() - sys.exit() - elif opt == '-i': - _log_file = arg - elif opt == '-o': - _json_file = arg - elif opt == '-v': - _vpp = arg - - if _log_file is None or _json_file is None or _vpp is None: - print_help() - sys.exit(1) - - try: - with open(_log_file, 'r') as input_file: - with open(_json_file, 'w') as output_file: - out = parse_tests(input_file, _vpp) - json.dump(out, fp=output_file, sort_keys=True, - indent=4, separators=(',', ': ')) - except IOError as ex_error: - print_error(str(ex_error)) - sys.exit(1) + args = parse_args() + + root = parse_tests(args) + ET.ElementTree.write(ET.ElementTree(root), args.output) if __name__ == "__main__": - main(sys.argv[1:]) + sys.exit(main())