1 # Copyright (c) 2017 Cisco and/or its affiliates.
2 # Licensed under the Apache License, Version 2.0 (the "License");
3 # you may not use this file except in compliance with the License.
4 # You may obtain a copy of the License at:
6 # http://www.apache.org/licenses/LICENSE-2.0
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS,
10 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 # See the License for the specific language governing permissions and
12 # limitations under the License.
14 """Algorithms to generate tables.
23 from string import replace
24 from math import isnan
25 from xml.etree import ElementTree as ET
27 from errors import PresentationError
28 from utils import mean, stdev, relative_change, remove_outliers, split_outliers
31 def generate_tables(spec, data):
32 """Generate all tables specified in the specification file.
34 :param spec: Specification read from the specification file.
35 :param data: Data to process.
36 :type spec: Specification
40 logging.info("Generating the tables ...")
41 for table in spec.tables:
43 eval(table["algorithm"])(table, data)
45 logging.error("The algorithm '{0}' is not defined.".
46 format(table["algorithm"]))
50 def table_details(table, input_data):
51 """Generate the table(s) with algorithm: table_detailed_test_results
52 specified in the specification file.
54 :param table: Table to generate.
55 :param input_data: Data to process.
56 :type table: pandas.Series
57 :type input_data: InputData
60 logging.info(" Generating the table {0} ...".
61 format(table.get("title", "")))
64 data = input_data.filter_data(table)
66 # Prepare the header of the tables
68 for column in table["columns"]:
69 header.append('"{0}"'.format(str(column["title"]).replace('"', '""')))
71 # Generate the data for the table according to the model in the table
73 job = table["data"].keys()[0]
74 build = str(table["data"][job][0])
76 suites = input_data.suites(job, build)
78 logging.error(" No data available. The table will not be generated.")
81 for suite_longname, suite in suites.iteritems():
83 suite_name = suite["name"]
85 for test in data[job][build].keys():
86 if data[job][build][test]["parent"] in suite_name:
88 for column in table["columns"]:
90 col_data = str(data[job][build][test][column["data"].
91 split(" ")[1]]).replace('"', '""')
92 if column["data"].split(" ")[1] in ("vat-history",
94 col_data = replace(col_data, " |br| ", "",
96 col_data = " |prein| {0} |preout| ".\
98 row_lst.append('"{0}"'.format(col_data))
100 row_lst.append("No data")
101 table_lst.append(row_lst)
103 # Write the data to file
105 file_name = "{0}_{1}{2}".format(table["output-file"], suite_name,
106 table["output-file-ext"])
107 logging.info(" Writing file: '{}'".format(file_name))
108 with open(file_name, "w") as file_handler:
109 file_handler.write(",".join(header) + "\n")
110 for item in table_lst:
111 file_handler.write(",".join(item) + "\n")
113 logging.info(" Done.")
116 def table_merged_details(table, input_data):
117 """Generate the table(s) with algorithm: table_merged_details
118 specified in the specification file.
120 :param table: Table to generate.
121 :param input_data: Data to process.
122 :type table: pandas.Series
123 :type input_data: InputData
126 logging.info(" Generating the table {0} ...".
127 format(table.get("title", "")))
130 data = input_data.filter_data(table)
131 data = input_data.merge_data(data)
132 data.sort_index(inplace=True)
134 suites = input_data.filter_data(table, data_set="suites")
135 suites = input_data.merge_data(suites)
137 # Prepare the header of the tables
139 for column in table["columns"]:
140 header.append('"{0}"'.format(str(column["title"]).replace('"', '""')))
142 for _, suite in suites.iteritems():
144 suite_name = suite["name"]
146 for test in data.keys():
147 if data[test]["parent"] in suite_name:
149 for column in table["columns"]:
151 col_data = str(data[test][column["data"].
152 split(" ")[1]]).replace('"', '""')
153 if column["data"].split(" ")[1] in ("vat-history",
155 col_data = replace(col_data, " |br| ", "",
157 col_data = " |prein| {0} |preout| ".\
158 format(col_data[:-5])
159 row_lst.append('"{0}"'.format(col_data))
161 row_lst.append("No data")
162 table_lst.append(row_lst)
164 # Write the data to file
166 file_name = "{0}_{1}{2}".format(table["output-file"], suite_name,
167 table["output-file-ext"])
168 logging.info(" Writing file: '{}'".format(file_name))
169 with open(file_name, "w") as file_handler:
170 file_handler.write(",".join(header) + "\n")
171 for item in table_lst:
172 file_handler.write(",".join(item) + "\n")
174 logging.info(" Done.")
177 def table_performance_improvements(table, input_data):
178 """Generate the table(s) with algorithm: table_performance_improvements
179 specified in the specification file.
181 :param table: Table to generate.
182 :param input_data: Data to process.
183 :type table: pandas.Series
184 :type input_data: InputData
187 def _write_line_to_file(file_handler, data):
188 """Write a line to the .csv file.
190 :param file_handler: File handler for the csv file. It must be open for
192 :param data: Item to be written to the file.
193 :type file_handler: BinaryIO
199 if isinstance(item["data"], str):
200 # Remove -?drdisc from the end
201 if item["data"].endswith("drdisc"):
202 item["data"] = item["data"][:-8]
203 line_lst.append(item["data"])
204 elif isinstance(item["data"], float):
205 line_lst.append("{:.1f}".format(item["data"]))
206 elif item["data"] is None:
208 file_handler.write(",".join(line_lst) + "\n")
210 logging.info(" Generating the table {0} ...".
211 format(table.get("title", "")))
214 file_name = table.get("template", None)
217 tmpl = _read_csv_template(file_name)
218 except PresentationError:
219 logging.error(" The template '{0}' does not exist. Skipping the "
220 "table.".format(file_name))
223 logging.error("The template is not defined. Skipping the table.")
227 data = input_data.filter_data(table)
229 # Prepare the header of the tables
231 for column in table["columns"]:
232 header.append(column["title"])
234 # Generate the data for the table according to the model in the table
237 for tmpl_item in tmpl:
239 for column in table["columns"]:
240 cmd = column["data"].split(" ")[0]
241 args = column["data"].split(" ")[1:]
242 if cmd == "template":
244 val = float(tmpl_item[int(args[0])])
246 val = tmpl_item[int(args[0])]
247 tbl_item.append({"data": val})
253 for build in data[job]:
255 data_lst.append(float(build[tmpl_item[0]]
256 ["throughput"]["value"]))
257 except (KeyError, TypeError):
261 tbl_item.append({"data": (eval(operation)(data_lst)) /
264 tbl_item.append({"data": None})
265 elif cmd == "operation":
268 nr1 = float(tbl_item[int(args[1])]["data"])
269 nr2 = float(tbl_item[int(args[2])]["data"])
271 tbl_item.append({"data": eval(operation)(nr1, nr2)})
273 tbl_item.append({"data": None})
274 except (IndexError, ValueError, TypeError):
275 logging.error("No data for {0}".format(tbl_item[0]["data"]))
276 tbl_item.append({"data": None})
279 logging.error("Not supported command {0}. Skipping the table.".
282 tbl_lst.append(tbl_item)
284 # Sort the table according to the relative change
285 tbl_lst.sort(key=lambda rel: rel[-1]["data"], reverse=True)
287 # Create the tables and write them to the files
289 "{0}_ndr_top{1}".format(table["output-file"], table["output-file-ext"]),
290 "{0}_pdr_top{1}".format(table["output-file"], table["output-file-ext"]),
291 "{0}_ndr_low{1}".format(table["output-file"], table["output-file-ext"]),
292 "{0}_pdr_low{1}".format(table["output-file"], table["output-file-ext"])
295 for file_name in file_names:
296 logging.info(" Writing the file '{0}'".format(file_name))
297 with open(file_name, "w") as file_handler:
298 file_handler.write(",".join(header) + "\n")
300 if isinstance(item[-1]["data"], float):
301 rel_change = round(item[-1]["data"], 1)
303 rel_change = item[-1]["data"]
304 if "ndr_top" in file_name \
305 and "ndr" in item[0]["data"] \
306 and rel_change >= 10.0:
307 _write_line_to_file(file_handler, item)
308 elif "pdr_top" in file_name \
309 and "pdr" in item[0]["data"] \
310 and rel_change >= 10.0:
311 _write_line_to_file(file_handler, item)
312 elif "ndr_low" in file_name \
313 and "ndr" in item[0]["data"] \
314 and rel_change < 10.0:
315 _write_line_to_file(file_handler, item)
316 elif "pdr_low" in file_name \
317 and "pdr" in item[0]["data"] \
318 and rel_change < 10.0:
319 _write_line_to_file(file_handler, item)
321 logging.info(" Done.")
324 def _read_csv_template(file_name):
325 """Read the template from a .csv file.
327 :param file_name: Name / full path / relative path of the file to read.
329 :returns: Data from the template as list (lines) of lists (items on line).
331 :raises: PresentationError if it is not possible to read the file.
335 with open(file_name, 'r') as csv_file:
337 for line in csv_file:
338 tmpl_data.append(line[:-1].split(","))
340 except IOError as err:
341 raise PresentationError(str(err), level="ERROR")
344 def table_performance_comparison(table, input_data):
345 """Generate the table(s) with algorithm: table_performance_comparison
346 specified in the specification file.
348 :param table: Table to generate.
349 :param input_data: Data to process.
350 :type table: pandas.Series
351 :type input_data: InputData
354 logging.info(" Generating the table {0} ...".
355 format(table.get("title", "")))
358 data = input_data.filter_data(table, continue_on_error=True)
360 # Prepare the header of the tables
362 header = ["Test case",
363 "{0} Throughput [Mpps]".format(table["reference"]["title"]),
364 "{0} stdev [Mpps]".format(table["reference"]["title"]),
365 "{0} Throughput [Mpps]".format(table["compare"]["title"]),
366 "{0} stdev [Mpps]".format(table["compare"]["title"]),
368 header_str = ",".join(header) + "\n"
369 except (AttributeError, KeyError) as err:
370 logging.error("The model is invalid, missing parameter: {0}".
374 # Prepare data to the table:
376 for job, builds in table["reference"]["data"].items():
378 for tst_name, tst_data in data[job][str(build)].iteritems():
379 if tbl_dict.get(tst_name, None) is None:
380 name = "{0}-{1}".format(tst_data["parent"].split("-")[0],
381 "-".join(tst_data["name"].
383 tbl_dict[tst_name] = {"name": name,
387 tbl_dict[tst_name]["ref-data"].\
388 append(tst_data["throughput"]["value"])
390 pass # No data in output.xml for this test
392 for job, builds in table["compare"]["data"].items():
394 for tst_name, tst_data in data[job][str(build)].iteritems():
396 tbl_dict[tst_name]["cmp-data"].\
397 append(tst_data["throughput"]["value"])
401 tbl_dict.pop(tst_name, None)
404 for tst_name in tbl_dict.keys():
405 item = [tbl_dict[tst_name]["name"], ]
406 if tbl_dict[tst_name]["ref-data"]:
407 data_t = remove_outliers(tbl_dict[tst_name]["ref-data"],
408 outlier_const=table["outlier-const"])
409 # TODO: Specify window size.
411 item.append(round(mean(data_t) / 1000000, 2))
412 item.append(round(stdev(data_t) / 1000000, 2))
414 item.extend([None, None])
416 item.extend([None, None])
417 if tbl_dict[tst_name]["cmp-data"]:
418 data_t = remove_outliers(tbl_dict[tst_name]["cmp-data"],
419 outlier_const=table["outlier-const"])
420 # TODO: Specify window size.
422 item.append(round(mean(data_t) / 1000000, 2))
423 item.append(round(stdev(data_t) / 1000000, 2))
425 item.extend([None, None])
427 item.extend([None, None])
428 if item[1] is not None and item[3] is not None:
429 item.append(int(relative_change(float(item[1]), float(item[3]))))
433 # Sort the table according to the relative change
434 tbl_lst.sort(key=lambda rel: rel[-1], reverse=True)
438 tbl_names = ["{0}-ndr-1t1c-full{1}".format(table["output-file"],
439 table["output-file-ext"]),
440 "{0}-ndr-2t2c-full{1}".format(table["output-file"],
441 table["output-file-ext"]),
442 "{0}-ndr-4t4c-full{1}".format(table["output-file"],
443 table["output-file-ext"]),
444 "{0}-pdr-1t1c-full{1}".format(table["output-file"],
445 table["output-file-ext"]),
446 "{0}-pdr-2t2c-full{1}".format(table["output-file"],
447 table["output-file-ext"]),
448 "{0}-pdr-4t4c-full{1}".format(table["output-file"],
449 table["output-file-ext"])
451 for file_name in tbl_names:
452 logging.info(" Writing file: '{0}'".format(file_name))
453 with open(file_name, "w") as file_handler:
454 file_handler.write(header_str)
456 if (file_name.split("-")[-3] in test[0] and # NDR vs PDR
457 file_name.split("-")[-2] in test[0]): # cores
458 test[0] = "-".join(test[0].split("-")[:-1])
459 file_handler.write(",".join([str(item) for item in test]) +
463 tbl_names_txt = ["{0}-ndr-1t1c-full.txt".format(table["output-file"]),
464 "{0}-ndr-2t2c-full.txt".format(table["output-file"]),
465 "{0}-ndr-4t4c-full.txt".format(table["output-file"]),
466 "{0}-pdr-1t1c-full.txt".format(table["output-file"]),
467 "{0}-pdr-2t2c-full.txt".format(table["output-file"]),
468 "{0}-pdr-4t4c-full.txt".format(table["output-file"])
471 for i, txt_name in enumerate(tbl_names_txt):
473 logging.info(" Writing file: '{0}'".format(txt_name))
474 with open(tbl_names[i], 'rb') as csv_file:
475 csv_content = csv.reader(csv_file, delimiter=',', quotechar='"')
476 for row in csv_content:
477 if txt_table is None:
478 txt_table = prettytable.PrettyTable(row)
480 txt_table.add_row(row)
481 txt_table.align["Test case"] = "l"
482 with open(txt_name, "w") as txt_file:
483 txt_file.write(str(txt_table))
485 # Selected tests in csv:
486 input_file = "{0}-ndr-1t1c-full{1}".format(table["output-file"],
487 table["output-file-ext"])
488 with open(input_file, "r") as in_file:
493 output_file = "{0}-ndr-1t1c-top{1}".format(table["output-file"],
494 table["output-file-ext"])
495 logging.info(" Writing file: '{0}'".format(output_file))
496 with open(output_file, "w") as out_file:
497 out_file.write(header_str)
498 for i, line in enumerate(lines[1:]):
499 if i == table["nr-of-tests-shown"]:
503 output_file = "{0}-ndr-1t1c-bottom{1}".format(table["output-file"],
504 table["output-file-ext"])
505 logging.info(" Writing file: '{0}'".format(output_file))
506 with open(output_file, "w") as out_file:
507 out_file.write(header_str)
508 for i, line in enumerate(lines[-1:0:-1]):
509 if i == table["nr-of-tests-shown"]:
513 input_file = "{0}-pdr-1t1c-full{1}".format(table["output-file"],
514 table["output-file-ext"])
515 with open(input_file, "r") as in_file:
520 output_file = "{0}-pdr-1t1c-top{1}".format(table["output-file"],
521 table["output-file-ext"])
522 logging.info(" Writing file: '{0}'".format(output_file))
523 with open(output_file, "w") as out_file:
524 out_file.write(header_str)
525 for i, line in enumerate(lines[1:]):
526 if i == table["nr-of-tests-shown"]:
530 output_file = "{0}-pdr-1t1c-bottom{1}".format(table["output-file"],
531 table["output-file-ext"])
532 logging.info(" Writing file: '{0}'".format(output_file))
533 with open(output_file, "w") as out_file:
534 out_file.write(header_str)
535 for i, line in enumerate(lines[-1:0:-1]):
536 if i == table["nr-of-tests-shown"]:
541 def table_performance_comparison_mrr(table, input_data):
542 """Generate the table(s) with algorithm: table_performance_comparison_mrr
543 specified in the specification file.
545 :param table: Table to generate.
546 :param input_data: Data to process.
547 :type table: pandas.Series
548 :type input_data: InputData
551 logging.info(" Generating the table {0} ...".
552 format(table.get("title", "")))
555 data = input_data.filter_data(table, continue_on_error=True)
557 # Prepare the header of the tables
559 header = ["Test case",
560 "{0} Throughput [Mpps]".format(table["reference"]["title"]),
561 "{0} stdev [Mpps]".format(table["reference"]["title"]),
562 "{0} Throughput [Mpps]".format(table["compare"]["title"]),
563 "{0} stdev [Mpps]".format(table["compare"]["title"]),
565 header_str = ",".join(header) + "\n"
566 except (AttributeError, KeyError) as err:
567 logging.error("The model is invalid, missing parameter: {0}".
571 # Prepare data to the table:
573 for job, builds in table["reference"]["data"].items():
575 for tst_name, tst_data in data[job][str(build)].iteritems():
576 if tbl_dict.get(tst_name, None) is None:
577 name = "{0}-{1}".format(tst_data["parent"].split("-")[0],
578 "-".join(tst_data["name"].
580 tbl_dict[tst_name] = {"name": name,
584 tbl_dict[tst_name]["ref-data"].\
585 append(tst_data["result"]["throughput"])
587 pass # No data in output.xml for this test
589 for job, builds in table["compare"]["data"].items():
591 for tst_name, tst_data in data[job][str(build)].iteritems():
593 tbl_dict[tst_name]["cmp-data"].\
594 append(tst_data["result"]["throughput"])
598 tbl_dict.pop(tst_name, None)
601 for tst_name in tbl_dict.keys():
602 item = [tbl_dict[tst_name]["name"], ]
603 if tbl_dict[tst_name]["ref-data"]:
604 data_t = remove_outliers(tbl_dict[tst_name]["ref-data"],
605 outlier_const=table["outlier-const"])
606 # TODO: Specify window size.
608 item.append(round(mean(data_t) / 1000000, 2))
609 item.append(round(stdev(data_t) / 1000000, 2))
611 item.extend([None, None])
613 item.extend([None, None])
614 if tbl_dict[tst_name]["cmp-data"]:
615 data_t = remove_outliers(tbl_dict[tst_name]["cmp-data"],
616 outlier_const=table["outlier-const"])
617 # TODO: Specify window size.
619 item.append(round(mean(data_t) / 1000000, 2))
620 item.append(round(stdev(data_t) / 1000000, 2))
622 item.extend([None, None])
624 item.extend([None, None])
625 if item[1] is not None and item[3] is not None and item[1] != 0:
626 item.append(int(relative_change(float(item[1]), float(item[3]))))
630 # Sort the table according to the relative change
631 tbl_lst.sort(key=lambda rel: rel[-1], reverse=True)
635 tbl_names = ["{0}-1t1c-full{1}".format(table["output-file"],
636 table["output-file-ext"]),
637 "{0}-2t2c-full{1}".format(table["output-file"],
638 table["output-file-ext"]),
639 "{0}-4t4c-full{1}".format(table["output-file"],
640 table["output-file-ext"])
642 for file_name in tbl_names:
643 logging.info(" Writing file: '{0}'".format(file_name))
644 with open(file_name, "w") as file_handler:
645 file_handler.write(header_str)
647 if file_name.split("-")[-2] in test[0]: # cores
648 test[0] = "-".join(test[0].split("-")[:-1])
649 file_handler.write(",".join([str(item) for item in test]) +
653 tbl_names_txt = ["{0}-1t1c-full.txt".format(table["output-file"]),
654 "{0}-2t2c-full.txt".format(table["output-file"]),
655 "{0}-4t4c-full.txt".format(table["output-file"])
658 for i, txt_name in enumerate(tbl_names_txt):
660 logging.info(" Writing file: '{0}'".format(txt_name))
661 with open(tbl_names[i], 'rb') as csv_file:
662 csv_content = csv.reader(csv_file, delimiter=',', quotechar='"')
663 for row in csv_content:
664 if txt_table is None:
665 txt_table = prettytable.PrettyTable(row)
667 txt_table.add_row(row)
668 txt_table.align["Test case"] = "l"
669 with open(txt_name, "w") as txt_file:
670 txt_file.write(str(txt_table))
673 def table_performance_trending_dashboard(table, input_data):
674 """Generate the table(s) with algorithm: table_performance_comparison
675 specified in the specification file.
677 :param table: Table to generate.
678 :param input_data: Data to process.
679 :type table: pandas.Series
680 :type input_data: InputData
683 logging.info(" Generating the table {0} ...".
684 format(table.get("title", "")))
687 data = input_data.filter_data(table, continue_on_error=True)
689 # Prepare the header of the tables
690 header = ["Test Case",
691 "Throughput Trend [Mpps]",
692 "Long Trend Compliance",
694 "Top Anomaly [Mpps]",
698 header_str = ",".join(header) + "\n"
700 # Prepare data to the table:
702 for job, builds in table["data"].items():
704 for tst_name, tst_data in data[job][str(build)].iteritems():
705 if tbl_dict.get(tst_name, None) is None:
706 name = "{0}-{1}".format(tst_data["parent"].split("-")[0],
707 "-".join(tst_data["name"].
709 tbl_dict[tst_name] = {"name": name,
712 tbl_dict[tst_name]["data"][str(build)] = \
713 tst_data["result"]["throughput"]
714 except (TypeError, KeyError):
715 pass # No data in output.xml for this test
718 for tst_name in tbl_dict.keys():
719 if len(tbl_dict[tst_name]["data"]) > 2:
721 pd_data = pd.Series(tbl_dict[tst_name]["data"])
722 win_size = min(pd_data.size, table["window"])
724 name = tbl_dict[tst_name]["name"]
726 median = pd_data.rolling(window=win_size, min_periods=2).median()
727 median_idx = pd_data.size - table["long-trend-window"]
728 median_idx = 0 if median_idx < 0 else median_idx
729 max_median = max(median.values[median_idx:])
730 trimmed_data, _ = split_outliers(pd_data, outlier_const=1.5,
732 stdev_t = pd_data.rolling(window=win_size, min_periods=2).std()
734 rel_change_lst = [None, ]
735 classification_lst = [None, ]
736 median_lst = [None, ]
737 sample_lst = [None, ]
739 for build_nr, value in pd_data.iteritems():
743 # Relative changes list:
744 if not isnan(value) \
745 and not isnan(median[build_nr]) \
746 and median[build_nr] != 0:
747 rel_change_lst.append(round(
748 relative_change(float(median[build_nr]), float(value)),
751 rel_change_lst.append(None)
753 # Classification list:
754 if isnan(trimmed_data[build_nr]) \
755 or isnan(median[build_nr]) \
756 or isnan(stdev_t[build_nr]) \
758 classification_lst.append("outlier")
759 elif value < (median[build_nr] - 3 * stdev_t[build_nr]):
760 classification_lst.append("regression")
761 elif value > (median[build_nr] + 3 * stdev_t[build_nr]):
762 classification_lst.append("progression")
764 classification_lst.append("normal")
765 sample_lst.append(value)
766 median_lst.append(median[build_nr])
768 last_idx = len(classification_lst) - 1
769 first_idx = last_idx - int(table["evaluated-window"])
774 consecutive_outliers = 0
776 for item in classification_lst[first_idx:]:
777 if item == "outlier":
779 consecutive_outliers += 1
780 if consecutive_outliers == 3:
783 consecutive_outliers = 0
786 classification = "failure"
787 elif "regression" in classification_lst[first_idx:]:
788 classification = "regression"
789 elif "progression" in classification_lst[first_idx:]:
790 classification = "progression"
792 classification = "normal"
794 if classification == "normal":
795 index = len(classification_lst) - 1
797 tmp_classification = "outlier" if classification == "failure" \
799 for idx in range(first_idx, len(classification_lst)):
800 if classification_lst[idx] == tmp_classification:
801 if rel_change_lst[idx]:
804 for idx in range(index+1, len(classification_lst)):
805 if classification_lst[idx] == tmp_classification:
806 if rel_change_lst[idx]:
807 if (abs(rel_change_lst[idx]) >
808 abs(rel_change_lst[index])):
811 trend = round(float(median_lst[-1]) / 1000000, 2) \
812 if not isnan(median_lst[-1]) else '-'
813 sample = round(float(sample_lst[index]) / 1000000, 2) \
814 if not isnan(sample_lst[index]) else '-'
815 rel_change = rel_change_lst[index] \
816 if rel_change_lst[index] is not None else '-'
817 if not isnan(max_median):
818 if not isnan(sample_lst[index]):
819 long_trend_threshold = max_median * \
820 (table["long-trend-threshold"] / 100)
821 if sample_lst[index] < long_trend_threshold:
822 long_trend_classification = "failure"
824 long_trend_classification = '-'
826 long_trend_classification = "failure"
828 long_trend_classification = '-'
829 tbl_lst.append([name,
831 long_trend_classification,
833 '-' if classification == "normal" else sample,
834 '-' if classification == "normal" else rel_change,
837 # Sort the table according to the classification
839 for long_trend_class in ("failure", '-'):
840 tbl_long = [item for item in tbl_lst if item[2] == long_trend_class]
841 for classification in \
842 ("failure", "regression", "progression", "normal"):
843 tbl_tmp = [item for item in tbl_long if item[3] == classification]
844 tbl_tmp.sort(key=lambda rel: rel[0])
845 tbl_sorted.extend(tbl_tmp)
847 file_name = "{0}{1}".format(table["output-file"], table["output-file-ext"])
849 logging.info(" Writing file: '{0}'".format(file_name))
850 with open(file_name, "w") as file_handler:
851 file_handler.write(header_str)
852 for test in tbl_sorted:
853 file_handler.write(",".join([str(item) for item in test]) + '\n')
855 txt_file_name = "{0}.txt".format(table["output-file"])
857 logging.info(" Writing file: '{0}'".format(txt_file_name))
858 with open(file_name, 'rb') as csv_file:
859 csv_content = csv.reader(csv_file, delimiter=',', quotechar='"')
860 for row in csv_content:
861 if txt_table is None:
862 txt_table = prettytable.PrettyTable(row)
864 txt_table.add_row(row)
865 txt_table.align["Test case"] = "l"
866 with open(txt_file_name, "w") as txt_file:
867 txt_file.write(str(txt_table))
870 def table_performance_trending_dashboard_html(table, input_data):
871 """Generate the table(s) with algorithm:
872 table_performance_trending_dashboard_html specified in the specification
875 :param table: Table to generate.
876 :param input_data: Data to process.
877 :type table: pandas.Series
878 :type input_data: InputData
881 logging.info(" Generating the table {0} ...".
882 format(table.get("title", "")))
885 with open(table["input-file"], 'rb') as csv_file:
886 csv_content = csv.reader(csv_file, delimiter=',', quotechar='"')
887 csv_lst = [item for item in csv_content]
889 logging.warning("The input file is not defined.")
891 except csv.Error as err:
892 logging.warning("Not possible to process the file '{0}'.\n{1}".
893 format(table["input-file"], err))
897 dashboard = ET.Element("table", attrib=dict(width="100%", border='0'))
900 tr = ET.SubElement(dashboard, "tr", attrib=dict(bgcolor="#7eade7"))
901 for idx, item in enumerate(csv_lst[0]):
902 alignment = "left" if idx == 0 else "center"
903 th = ET.SubElement(tr, "th", attrib=dict(align=alignment))
907 for r_idx, row in enumerate(csv_lst[1:]):
908 background = "#D4E4F7" if r_idx % 2 else "white"
909 tr = ET.SubElement(dashboard, "tr", attrib=dict(bgcolor=background))
912 for c_idx, item in enumerate(row):
913 alignment = "left" if c_idx == 0 else "center"
914 td = ET.SubElement(tr, "td", attrib=dict(align=alignment))
922 file_name = "container_memif.html"
924 elif "vhost" in item:
925 if "l2xcbase" in item or "l2bdbasemaclrn" in item:
926 file_name = "vm_vhost_l2.html"
927 elif "ip4base" in item:
928 file_name = "vm_vhost_ip4.html"
930 elif "ipsec" in item:
931 file_name = "ipsec.html"
933 elif "ethip4lispip" in item or "ethip4vxlan" in item:
934 file_name = "ip4_tunnels.html"
936 elif "ip4base" in item or "ip4scale" in item:
937 file_name = "ip4.html"
938 if "iacl" in item or "snat" in item or "cop" in item:
939 feature = "-features"
941 elif "ip6base" in item or "ip6scale" in item:
942 file_name = "ip6.html"
944 elif "l2xcbase" in item or "l2xcscale" in item \
945 or "l2bdbasemaclrn" in item or "l2bdscale" in item \
946 or "l2dbbasemaclrn" in item or "l2dbscale" in item:
947 file_name = "l2.html"
949 feature = "-features"
955 elif "xl710" in item:
964 elif "9000b" in item:
976 url = url + file_name + anchor + feature
978 ref = ET.SubElement(td, "a", attrib=dict(href=url))
982 if item == "regression":
983 td.set("bgcolor", "#eca1a6")
984 elif item == "failure":
985 td.set("bgcolor", "#d6cbd3")
986 elif item == "progression":
987 td.set("bgcolor", "#bdcebe")
992 with open(table["output-file"], 'w') as html_file:
993 logging.info(" Writing file: '{0}'".
994 format(table["output-file"]))
995 html_file.write(".. raw:: html\n\n\t")
996 html_file.write(ET.tostring(dashboard))
997 html_file.write("\n\t<p><br><br></p>\n")
999 logging.warning("The output file is not defined.")