X-Git-Url: https://gerrit.fd.io/r/gitweb?a=blobdiff_plain;f=resources%2Ftools%2Fpresentation%2Fgenerator_plots.py;h=b3e28dda8b6a85ca22eca946509f1546fb6a4bbb;hb=019a796394d6b8484225fe133c24c0e502b219f6;hp=32f146bca84d5e412a1cc7ee6759496b1f0fd896;hpb=670a905fcf26395e2064aab79449fe582eec5853;p=csit.git
diff --git a/resources/tools/presentation/generator_plots.py b/resources/tools/presentation/generator_plots.py
index 32f146bca8..b3e28dda8b 100644
--- a/resources/tools/presentation/generator_plots.py
+++ b/resources/tools/presentation/generator_plots.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2018 Cisco and/or its affiliates.
+# Copyright (c) 2019 Cisco and/or its affiliates.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at:
@@ -15,6 +15,7 @@
"""
+import re
import logging
import pandas as pd
import plotly.offline as ploff
@@ -24,7 +25,7 @@ from plotly.exceptions import PlotlyError
from collections import OrderedDict
from copy import deepcopy
-from utils import mean
+from utils import mean, stdev
COLORS = ["SkyBlue", "Olive", "Purple", "Coral", "Indigo", "Pink",
@@ -34,6 +35,8 @@ COLORS = ["SkyBlue", "Olive", "Purple", "Coral", "Indigo", "Pink",
"LightGreen", "LightSeaGreen", "LightSkyBlue", "Maroon",
"MediumSeaGreen", "SeaGreen", "LightSlateGrey"]
+REGEX_NIC = re.compile(r'\d*ge\dp\d\D*\d*-')
+
def generate_plots(spec, data):
"""Generate all plots specified in the specification file.
@@ -144,21 +147,14 @@ def plot_performance_box(plot, input_data):
df.head()
y_max = list()
for i, col in enumerate(df.columns):
+ tst_name = re.sub(REGEX_NIC, "",
+ col.lower().replace('-ndrpdr', '').
+ replace('2n1l-', ''))
name = "{nr}. ({samples:02d} run{plural}) {name}".\
format(nr=(i + 1),
samples=nr_of_samples[i],
plural='s' if nr_of_samples[i] > 1 else '',
- name=col.lower().replace('-ndrpdr', ''))
- if len(name) > 50:
- name_lst = name.split('-')
- name = ""
- split_name = True
- for segment in name_lst:
- if (len(name) + len(segment) + 1) > 50 and split_name:
- name += "
"
- split_name = False
- name += segment + '-'
- name = name[:-1]
+ name=tst_name)
logging.debug(name)
traces.append(plgo.Box(x=[str(i + 1) + '.'] * len(df[col]),
@@ -177,7 +173,7 @@ def plot_performance_box(plot, input_data):
# Create plot
layout = deepcopy(plot["layout"])
if layout.get("title", None):
- layout["title"] = "Packet Throughput: {0}". \
+ layout["title"] = "Throughput: {0}". \
format(layout["title"])
if y_max:
layout["yaxis"]["range"] = [0, max(y_max)]
@@ -195,6 +191,247 @@ def plot_performance_box(plot, input_data):
return
+def plot_soak_bars(plot, input_data):
+ """Generate the plot(s) with algorithm: plot_soak_bars
+ specified in the specification file.
+
+ :param plot: Plot to generate.
+ :param input_data: Data to process.
+ :type plot: pandas.Series
+ :type input_data: InputData
+ """
+
+ # Transform the data
+ plot_title = plot.get("title", "")
+ logging.info(" Creating the data set for the {0} '{1}'.".
+ format(plot.get("type", ""), plot_title))
+ data = input_data.filter_data(plot)
+ if data is None:
+ logging.error("No data.")
+ return
+
+ # Prepare the data for the plot
+ y_vals = dict()
+ y_tags = dict()
+ for job in data:
+ for build in job:
+ for test in build:
+ if y_vals.get(test["parent"], None) is None:
+ y_tags[test["parent"]] = test.get("tags", None)
+ try:
+ if test["type"] in ("SOAK", ):
+ y_vals[test["parent"]] = test["throughput"]
+ else:
+ continue
+ except (KeyError, TypeError):
+ y_vals[test["parent"]] = dict()
+
+ # Sort the tests
+ order = plot.get("sort", None)
+ if order and y_tags:
+ y_sorted = OrderedDict()
+ y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
+ for tag in order:
+ logging.debug(tag)
+ for suite, tags in y_tags_l.items():
+ if "not " in tag:
+ tag = tag.split(" ")[-1]
+ if tag.lower() in tags:
+ continue
+ else:
+ if tag.lower() not in tags:
+ continue
+ try:
+ y_sorted[suite] = y_vals.pop(suite)
+ y_tags_l.pop(suite)
+ logging.debug(suite)
+ except KeyError as err:
+ logging.error("Not found: {0}".format(repr(err)))
+ finally:
+ break
+ else:
+ y_sorted = y_vals
+
+ idx = 0
+ y_max = 0
+ traces = list()
+ for test_name, test_data in y_sorted.items():
+ idx += 1
+ name = "{nr}. {name}".\
+ format(nr=idx, name=test_name.lower().replace('-soak', ''))
+ if len(name) > 50:
+ name_lst = name.split('-')
+ name = ""
+ split_name = True
+ for segment in name_lst:
+ if (len(name) + len(segment) + 1) > 50 and split_name:
+ name += "
"
+ split_name = False
+ name += segment + '-'
+ name = name[:-1]
+
+ y_val = test_data.get("LOWER", None)
+ if y_val:
+ y_val /= 1000000
+ if y_val > y_max:
+ y_max = y_val
+
+ time = "No Information"
+ result = "No Information"
+ hovertext = ("{name}
"
+ "Packet Throughput: {val:.2f}Mpps
"
+ "Final Duration: {time}
"
+ "Result: {result}".format(name=name,
+ val=y_val,
+ time=time,
+ result=result))
+ traces.append(plgo.Bar(x=[str(idx) + '.', ],
+ y=[y_val, ],
+ name=name,
+ text=hovertext,
+ hoverinfo="text"))
+ try:
+ # Create plot
+ layout = deepcopy(plot["layout"])
+ if layout.get("title", None):
+ layout["title"] = "Packet Throughput: {0}". \
+ format(layout["title"])
+ if y_max:
+ layout["yaxis"]["range"] = [0, y_max + 1]
+ plpl = plgo.Figure(data=traces, layout=layout)
+ # Export Plot
+ logging.info(" Writing file '{0}{1}'.".
+ format(plot["output-file"], plot["output-file-type"]))
+ ploff.plot(plpl, show_link=False, auto_open=False,
+ filename='{0}{1}'.format(plot["output-file"],
+ plot["output-file-type"]))
+ except PlotlyError as err:
+ logging.error(" Finished with error: {}".
+ format(repr(err).replace("\n", " ")))
+ return
+
+
+def plot_soak_boxes(plot, input_data):
+ """Generate the plot(s) with algorithm: plot_soak_boxes
+ specified in the specification file.
+
+ :param plot: Plot to generate.
+ :param input_data: Data to process.
+ :type plot: pandas.Series
+ :type input_data: InputData
+ """
+
+ # Transform the data
+ plot_title = plot.get("title", "")
+ logging.info(" Creating the data set for the {0} '{1}'.".
+ format(plot.get("type", ""), plot_title))
+ data = input_data.filter_data(plot)
+ if data is None:
+ logging.error("No data.")
+ return
+
+ # Prepare the data for the plot
+ y_vals = dict()
+ y_tags = dict()
+ for job in data:
+ for build in job:
+ for test in build:
+ if y_vals.get(test["parent"], None) is None:
+ y_tags[test["parent"]] = test.get("tags", None)
+ try:
+ if test["type"] in ("SOAK", ):
+ y_vals[test["parent"]] = test["throughput"]
+ else:
+ continue
+ except (KeyError, TypeError):
+ y_vals[test["parent"]] = dict()
+
+ # Sort the tests
+ order = plot.get("sort", None)
+ if order and y_tags:
+ y_sorted = OrderedDict()
+ y_tags_l = {s: [t.lower() for t in ts] for s, ts in y_tags.items()}
+ for tag in order:
+ logging.debug(tag)
+ for suite, tags in y_tags_l.items():
+ if "not " in tag:
+ tag = tag.split(" ")[-1]
+ if tag.lower() in tags:
+ continue
+ else:
+ if tag.lower() not in tags:
+ continue
+ try:
+ y_sorted[suite] = y_vals.pop(suite)
+ y_tags_l.pop(suite)
+ logging.debug(suite)
+ except KeyError as err:
+ logging.error("Not found: {0}".format(repr(err)))
+ finally:
+ break
+ else:
+ y_sorted = y_vals
+
+ idx = 0
+ y_max = 0
+ traces = list()
+ for test_name, test_data in y_sorted.items():
+ idx += 1
+ name = "{nr}. {name}".\
+ format(nr=idx, name=test_name.lower().replace('-soak', '').
+ replace('2n1l-', ''))
+ if len(name) > 55:
+ name_lst = name.split('-')
+ name = ""
+ split_name = True
+ for segment in name_lst:
+ if (len(name) + len(segment) + 1) > 55 and split_name:
+ name += "
"
+ split_name = False
+ name += segment + '-'
+ name = name[:-1]
+
+ y_val = test_data.get("UPPER", None)
+ if y_val:
+ y_val /= 1000000
+ if y_val > y_max:
+ y_max = y_val
+
+ y_base = test_data.get("LOWER", None)
+ if y_base:
+ y_base /= 1000000
+
+ hovertext = ("Upper bound: {upper:.2f}
"
+ "Lower bound: {lower:.2f}".format(upper=y_val,
+ lower=y_base))
+ traces.append(plgo.Bar(x=[str(idx) + '.', ],
+ # +0.05 to see the value in case lower == upper
+ y=[y_val - y_base + 0.05, ],
+ base=y_base,
+ name=name,
+ text=hovertext,
+ hoverinfo="text"))
+ try:
+ # Create plot
+ layout = deepcopy(plot["layout"])
+ if layout.get("title", None):
+ layout["title"] = "Throughput: {0}". \
+ format(layout["title"])
+ if y_max:
+ layout["yaxis"]["range"] = [0, y_max + 1]
+ plpl = plgo.Figure(data=traces, layout=layout)
+ # Export Plot
+ logging.info(" Writing file '{0}{1}'.".
+ format(plot["output-file"], plot["output-file-type"]))
+ ploff.plot(plpl, show_link=False, auto_open=False,
+ filename='{0}{1}'.format(plot["output-file"],
+ plot["output-file-type"]))
+ except PlotlyError as err:
+ logging.error(" Finished with error: {}".
+ format(repr(err).replace("\n", " ")))
+ return
+
+
def plot_latency_error_bars(plot, input_data):
"""Generate the plot(s) with algorithm: plot_latency_error_bars
specified in the specification file.
@@ -298,17 +535,8 @@ def plot_latency_error_bars(plot, input_data):
y_maxs = list()
nr_of_samples = list()
for key, val in y_sorted.items():
- name = "-".join(key.split("-")[1:-1])
- if len(name) > 50:
- name_lst = name.split('-')
- name = ""
- split_name = True
- for segment in name_lst:
- if (len(name) + len(segment) + 1) > 50 and split_name:
- name += "
"
- split_name = False
- name += segment + '-'
- name = name[:-1]
+ name = re.sub(REGEX_NIC, "", key.replace('-ndrpdr', '').
+ replace('2n1l-', ''))
x_vals.append(name) # dir 1
y_vals.append(mean(val[1]) if val[1] else None)
y_mins.append(mean(val[0]) if val[0] else None)
@@ -398,7 +626,7 @@ def plot_latency_error_bars(plot, input_data):
format(plot["output-file"], plot["output-file-type"]))
layout = deepcopy(plot["layout"])
if layout.get("title", None):
- layout["title"] = "Packet Latency: {0}".\
+ layout["title"] = "Latency: {0}".\
format(layout["title"])
layout["annotations"] = annotations
plpl = plgo.Figure(data=traces, layout=layout)
@@ -487,18 +715,8 @@ def plot_throughput_speedup_analysis(plot, input_data):
for test_name, test_vals in y_vals.items():
try:
if test_vals["1"][1]:
- name = "-".join(test_name.split('-')[1:-1])
- if len(name) > 50:
- name_lst = name.split('-')
- name = ""
- split_name = True
- for segment in name_lst:
- if (len(name) + len(segment) + 1) > 50 and split_name:
- name += "
"
- split_name = False
- name += segment + '-'
- name = name[:-1]
-
+ name = re.sub(REGEX_NIC, "", test_name.replace('-ndrpdr', '').
+ replace('2n1l-', ''))
vals[name] = dict()
y_val_1 = test_vals["1"][0] / 1000000.0
y_val_2 = test_vals["2"][0] / 1000000.0 if test_vals["2"][0] \
@@ -518,12 +736,14 @@ def plot_throughput_speedup_analysis(plot, input_data):
test_vals["4"][1]]
try:
- val_max = max(max(vals[name]["val"], vals[name]["ideal"]))
+ # val_max = max(max(vals[name]["val"], vals[name]["ideal"]))
+ val_max = max(vals[name]["val"])
except ValueError as err:
logging.error(err)
continue
if val_max:
- y_max.append(int((val_max / 10) + 1) * 10)
+ # y_max.append(int((val_max / 10) + 1) * 10)
+ y_max.append(val_max)
if y_val_2:
vals[name]["rel"][1] = round(y_val_2 / y_val_1, 2)
@@ -575,7 +795,9 @@ def plot_throughput_speedup_analysis(plot, input_data):
for tag in order:
for test, tags in y_tags_l.items():
if tag.lower() in tags:
- name = "-".join(test.split('-')[1:-1])
+ name = re.sub(REGEX_NIC, "",
+ test.replace('-ndrpdr', '').
+ replace('2n1l-', ''))
try:
y_sorted[name] = vals.pop(name)
y_tags_l.pop(test)
@@ -597,35 +819,36 @@ def plot_throughput_speedup_analysis(plot, input_data):
logging.error(err)
return
nic_limit /= 1000000.0
- if nic_limit < threshold:
- traces.append(plgo.Scatter(
- x=x_vals,
- y=[nic_limit, ] * len(x_vals),
- name="NIC: {0:.2f}Mpps".format(nic_limit),
- showlegend=False,
- mode="lines",
- line=dict(
- dash="dot",
- color=COLORS[-1],
- width=1),
- hoverinfo="none"
- ))
- annotations.append(dict(
- x=1,
- y=nic_limit,
- xref="x",
- yref="y",
- xanchor="left",
- yanchor="bottom",
- text="NIC: {0:.2f}Mpps".format(nic_limit),
- font=dict(
- size=14,
- color=COLORS[-1],
- ),
- align="left",
- showarrow=False
- ))
- y_max.append(int((nic_limit / 10) + 1) * 10)
+ # if nic_limit < threshold:
+ traces.append(plgo.Scatter(
+ x=x_vals,
+ y=[nic_limit, ] * len(x_vals),
+ name="NIC: {0:.2f}Mpps".format(nic_limit),
+ showlegend=False,
+ mode="lines",
+ line=dict(
+ dash="dot",
+ color=COLORS[-1],
+ width=1),
+ hoverinfo="none"
+ ))
+ annotations.append(dict(
+ x=1,
+ y=nic_limit,
+ xref="x",
+ yref="y",
+ xanchor="left",
+ yanchor="bottom",
+ text="NIC: {0:.2f}Mpps".format(nic_limit),
+ font=dict(
+ size=14,
+ color=COLORS[-1],
+ ),
+ align="left",
+ showarrow=False
+ ))
+ # y_max.append(int((nic_limit / 10) + 1) * 10)
+ y_max.append(nic_limit)
lnk_limit /= 1000000.0
if lnk_limit < threshold:
@@ -656,10 +879,12 @@ def plot_throughput_speedup_analysis(plot, input_data):
align="left",
showarrow=False
))
- y_max.append(int((lnk_limit / 10) + 1) * 10)
+ # y_max.append(int((lnk_limit / 10) + 1) * 10)
+ y_max.append(lnk_limit)
pci_limit /= 1000000.0
- if pci_limit < threshold:
+ if (pci_limit < threshold and
+ (pci_limit < lnk_limit * 0.95 or lnk_limit > lnk_limit * 1.05)):
traces.append(plgo.Scatter(
x=x_vals,
y=[pci_limit, ] * len(x_vals),
@@ -687,7 +912,8 @@ def plot_throughput_speedup_analysis(plot, input_data):
align="left",
showarrow=False
))
- y_max.append(int((pci_limit / 10) + 1) * 10)
+ # y_max.append(int((pci_limit / 10) + 1) * 10)
+ y_max.append(pci_limit)
# Perfect and measured:
cidx = 0
@@ -747,6 +973,8 @@ def plot_throughput_speedup_analysis(plot, input_data):
if layout.get("title", None):
layout["title"] = "Speedup Multi-core: {0}". \
format(layout["title"])
+ # layout["yaxis"]["range"] = [0, int((max(y_max) / 10) + 1) * 10]
+ layout["yaxis"]["range"] = [0, int(max(y_max) * 1.1)]
layout["annotations"].extend(annotations)
plpl = plgo.Figure(data=traces, layout=layout)
@@ -841,3 +1069,741 @@ def plot_http_server_performance_box(plot, input_data):
logging.error(" Finished with error: {}".
format(str(err).replace("\n", " ")))
return
+
+
+def plot_service_density_heatmap(plot, input_data):
+ """Generate the plot(s) with algorithm: plot_service_density_heatmap
+ specified in the specification file.
+
+ :param plot: Plot to generate.
+ :param input_data: Data to process.
+ :type plot: pandas.Series
+ :type input_data: InputData
+ """
+
+ REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
+ REGEX_TEST_NAME = re.compile(r'^.*-(\d+vhost|\d+memif)-'
+ r'(\d+chain|\d+pipe)-'
+ r'(\d+vm|\d+dcr|\d+drc).*$')
+
+ txt_chains = list()
+ txt_nodes = list()
+ vals = dict()
+
+ # Transform the data
+ logging.info(" Creating the data set for the {0} '{1}'.".
+ format(plot.get("type", ""), plot.get("title", "")))
+ data = input_data.filter_data(plot, continue_on_error=True)
+ if data is None or data.empty:
+ logging.error("No data.")
+ return
+
+ for job in data:
+ for build in job:
+ for test in build:
+ for tag in test['tags']:
+ groups = re.search(REGEX_CN, tag)
+ if groups:
+ c = str(groups.group(1))
+ n = str(groups.group(2))
+ break
+ else:
+ continue
+ groups = re.search(REGEX_TEST_NAME, test["name"])
+ if groups and len(groups.groups()) == 3:
+ hover_name = "{vhost}-{chain}-{vm}".format(
+ vhost=str(groups.group(1)),
+ chain=str(groups.group(2)),
+ vm=str(groups.group(3)))
+ else:
+ hover_name = ""
+ if vals.get(c, None) is None:
+ vals[c] = dict()
+ if vals[c].get(n, None) is None:
+ vals[c][n] = dict(name=hover_name,
+ vals=list(),
+ nr=None,
+ mean=None,
+ stdev=None)
+ try:
+ if plot["include-tests"] == "MRR":
+ result = test["result"]["receive-rate"].avg
+ elif plot["include-tests"] == "PDR":
+ result = test["throughput"]["PDR"]["LOWER"]
+ elif plot["include-tests"] == "NDR":
+ result = test["throughput"]["NDR"]["LOWER"]
+ else:
+ result = None
+ except TypeError:
+ result = None
+
+ if result:
+ vals[c][n]["vals"].append(result)
+
+ if not vals:
+ logging.error("No data.")
+ return
+
+ for key_c in vals.keys():
+ txt_chains.append(key_c)
+ for key_n in vals[key_c].keys():
+ txt_nodes.append(key_n)
+ if vals[key_c][key_n]["vals"]:
+ vals[key_c][key_n]["nr"] = len(vals[key_c][key_n]["vals"])
+ vals[key_c][key_n]["mean"] = \
+ round(mean(vals[key_c][key_n]["vals"]) / 1000000, 1)
+ vals[key_c][key_n]["stdev"] = \
+ round(stdev(vals[key_c][key_n]["vals"]) / 1000000, 1)
+ txt_nodes = list(set(txt_nodes))
+
+ txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
+ txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
+
+ chains = [i + 1 for i in range(len(txt_chains))]
+ nodes = [i + 1 for i in range(len(txt_nodes))]
+
+ data = [list() for _ in range(len(chains))]
+ for c in chains:
+ for n in nodes:
+ try:
+ val = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean"]
+ except (KeyError, IndexError):
+ val = None
+ data[c - 1].append(val)
+
+ # Colorscales:
+ my_green = [[0.0, 'rgb(235, 249, 242)'],
+ [1.0, 'rgb(45, 134, 89)']]
+
+ my_blue = [[0.0, 'rgb(236, 242, 248)'],
+ [1.0, 'rgb(57, 115, 172)']]
+
+ my_grey = [[0.0, 'rgb(230, 230, 230)'],
+ [1.0, 'rgb(102, 102, 102)']]
+
+ hovertext = list()
+ annotations = list()
+
+ text = ("Test: {name}
"
+ "Runs: {nr}
"
+ "Thput: {val}
"
+ "StDev: {stdev}")
+
+ for c in range(len(txt_chains)):
+ hover_line = list()
+ for n in range(len(txt_nodes)):
+ if data[c][n] is not None:
+ annotations.append(dict(
+ x=n+1,
+ y=c+1,
+ xref="x",
+ yref="y",
+ xanchor="center",
+ yanchor="middle",
+ text=str(data[c][n]),
+ font=dict(
+ size=14,
+ ),
+ align="center",
+ showarrow=False
+ ))
+ hover_line.append(text.format(
+ name=vals[txt_chains[c]][txt_nodes[n]]["name"],
+ nr=vals[txt_chains[c]][txt_nodes[n]]["nr"],
+ val=data[c][n],
+ stdev=vals[txt_chains[c]][txt_nodes[n]]["stdev"]))
+ hovertext.append(hover_line)
+
+ traces = [
+ plgo.Heatmap(x=nodes,
+ y=chains,
+ z=data,
+ colorbar=dict(
+ title=plot.get("z-axis", ""),
+ titleside="right",
+ titlefont=dict(
+ size=16
+ ),
+ tickfont=dict(
+ size=16,
+ ),
+ tickformat=".1f",
+ yanchor="bottom",
+ y=-0.02,
+ len=0.925,
+ ),
+ showscale=True,
+ colorscale=my_green,
+ text=hovertext,
+ hoverinfo="text")
+ ]
+
+ for idx, item in enumerate(txt_nodes):
+ # X-axis, numbers:
+ annotations.append(dict(
+ x=idx+1,
+ y=0.05,
+ xref="x",
+ yref="y",
+ xanchor="center",
+ yanchor="top",
+ text=item,
+ font=dict(
+ size=16,
+ ),
+ align="center",
+ showarrow=False
+ ))
+ for idx, item in enumerate(txt_chains):
+ # Y-axis, numbers:
+ annotations.append(dict(
+ x=0.35,
+ y=idx+1,
+ xref="x",
+ yref="y",
+ xanchor="right",
+ yanchor="middle",
+ text=item,
+ font=dict(
+ size=16,
+ ),
+ align="center",
+ showarrow=False
+ ))
+ # X-axis, title:
+ annotations.append(dict(
+ x=0.55,
+ y=-0.15,
+ xref="paper",
+ yref="y",
+ xanchor="center",
+ yanchor="bottom",
+ text=plot.get("x-axis", ""),
+ font=dict(
+ size=16,
+ ),
+ align="center",
+ showarrow=False
+ ))
+ # Y-axis, title:
+ annotations.append(dict(
+ x=-0.1,
+ y=0.5,
+ xref="x",
+ yref="paper",
+ xanchor="center",
+ yanchor="middle",
+ text=plot.get("y-axis", ""),
+ font=dict(
+ size=16,
+ ),
+ align="center",
+ textangle=270,
+ showarrow=False
+ ))
+ updatemenus = list([
+ dict(
+ x=1.0,
+ y=0.0,
+ xanchor='right',
+ yanchor='bottom',
+ direction='up',
+ buttons=list([
+ dict(
+ args=[{"colorscale": [my_green, ], "reversescale": False}],
+ label="Green",
+ method="update"
+ ),
+ dict(
+ args=[{"colorscale": [my_blue, ], "reversescale": False}],
+ label="Blue",
+ method="update"
+ ),
+ dict(
+ args=[{"colorscale": [my_grey, ], "reversescale": False}],
+ label="Grey",
+ method="update"
+ )
+ ])
+ )
+ ])
+
+ try:
+ layout = deepcopy(plot["layout"])
+ except KeyError as err:
+ logging.error("Finished with error: No layout defined")
+ logging.error(repr(err))
+ return
+
+ layout["annotations"] = annotations
+ layout['updatemenus'] = updatemenus
+
+ try:
+ # Create plot
+ plpl = plgo.Figure(data=traces, layout=layout)
+
+ # Export Plot
+ logging.info(" Writing file '{0}{1}'.".
+ format(plot["output-file"], plot["output-file-type"]))
+ ploff.plot(plpl, show_link=False, auto_open=False,
+ filename='{0}{1}'.format(plot["output-file"],
+ plot["output-file-type"]))
+ except PlotlyError as err:
+ logging.error(" Finished with error: {}".
+ format(str(err).replace("\n", " ")))
+ return
+
+
+def plot_service_density_heatmap_compare(plot, input_data):
+ """Generate the plot(s) with algorithm: plot_service_density_heatmap_compare
+ specified in the specification file.
+
+ :param plot: Plot to generate.
+ :param input_data: Data to process.
+ :type plot: pandas.Series
+ :type input_data: InputData
+ """
+
+ REGEX_CN = re.compile(r'^(\d*)R(\d*)C$')
+ REGEX_TEST_NAME = re.compile(r'^.*-(\d+ch|\d+pl)-'
+ r'(\d+vh|\d+mif)-'
+ r'(\d+vm|\d+dcr).*$')
+ REGEX_THREADS = re.compile(r'^(\d+)(VM|DCR)(\d+)T$')
+
+ txt_chains = list()
+ txt_nodes = list()
+ vals = dict()
+
+ # Transform the data
+ logging.info(" Creating the data set for the {0} '{1}'.".
+ format(plot.get("type", ""), plot.get("title", "")))
+ data = input_data.filter_data(plot, continue_on_error=True)
+ if data is None or data.empty:
+ logging.error("No data.")
+ return
+
+ for job in data:
+ for build in job:
+ for test in build:
+ for tag in test['tags']:
+ groups = re.search(REGEX_CN, tag)
+ if groups:
+ c = str(groups.group(1))
+ n = str(groups.group(2))
+ break
+ else:
+ continue
+ groups = re.search(REGEX_TEST_NAME, test["name"])
+ if groups and len(groups.groups()) == 3:
+ hover_name = "{chain}-{vhost}-{vm}".format(
+ chain=str(groups.group(1)),
+ vhost=str(groups.group(2)),
+ vm=str(groups.group(3)))
+ else:
+ hover_name = ""
+ if vals.get(c, None) is None:
+ vals[c] = dict()
+ if vals[c].get(n, None) is None:
+ vals[c][n] = dict(name=hover_name,
+ vals_r=list(),
+ vals_c=list(),
+ nr_r=None,
+ nr_c=None,
+ mean_r=None,
+ mean_c=None,
+ stdev_r=None,
+ stdev_c=None)
+ try:
+ if plot["include-tests"] == "MRR":
+ result = test["result"]["receive-rate"].avg
+ elif plot["include-tests"] == "PDR":
+ result = test["throughput"]["PDR"]["LOWER"]
+ elif plot["include-tests"] == "NDR":
+ result = test["throughput"]["NDR"]["LOWER"]
+ else:
+ result = None
+ except TypeError:
+ result = None
+
+ if result:
+ for tag in test['tags']:
+ groups = re.search(REGEX_THREADS, tag)
+ if groups and len(groups.groups()) == 3:
+ if str(groups.group(3)) == \
+ plot["reference"]["include"]:
+ vals[c][n]["vals_r"].append(result)
+ elif str(groups.group(3)) == \
+ plot["compare"]["include"]:
+ vals[c][n]["vals_c"].append(result)
+ break
+ if not vals:
+ logging.error("No data.")
+ return
+
+ for key_c in vals.keys():
+ txt_chains.append(key_c)
+ for key_n in vals[key_c].keys():
+ txt_nodes.append(key_n)
+ if vals[key_c][key_n]["vals_r"]:
+ vals[key_c][key_n]["nr_r"] = len(vals[key_c][key_n]["vals_r"])
+ vals[key_c][key_n]["mean_r"] = \
+ mean(vals[key_c][key_n]["vals_r"])
+ vals[key_c][key_n]["stdev_r"] = \
+ round(stdev(vals[key_c][key_n]["vals_r"]) / 1000000, 1)
+ if vals[key_c][key_n]["vals_c"]:
+ vals[key_c][key_n]["nr_c"] = len(vals[key_c][key_n]["vals_c"])
+ vals[key_c][key_n]["mean_c"] = \
+ mean(vals[key_c][key_n]["vals_c"])
+ vals[key_c][key_n]["stdev_c"] = \
+ round(stdev(vals[key_c][key_n]["vals_c"]) / 1000000, 1)
+
+ txt_nodes = list(set(txt_nodes))
+
+ txt_chains = sorted(txt_chains, key=lambda chain: int(chain))
+ txt_nodes = sorted(txt_nodes, key=lambda node: int(node))
+
+ chains = [i + 1 for i in range(len(txt_chains))]
+ nodes = [i + 1 for i in range(len(txt_nodes))]
+
+ data_r = [list() for _ in range(len(chains))]
+ data_c = [list() for _ in range(len(chains))]
+ diff = [list() for _ in range(len(chains))]
+ for c in chains:
+ for n in nodes:
+ try:
+ val_r = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_r"]
+ except (KeyError, IndexError):
+ val_r = None
+ try:
+ val_c = vals[txt_chains[c - 1]][txt_nodes[n - 1]]["mean_c"]
+ except (KeyError, IndexError):
+ val_c = None
+ if val_c is not None and val_r:
+ val_d = (val_c - val_r) * 100 / val_r
+ else:
+ val_d = None
+
+ if val_r is not None:
+ val_r = round(val_r / 1000000, 1)
+ data_r[c - 1].append(val_r)
+ if val_c is not None:
+ val_c = round(val_c / 1000000, 1)
+ data_c[c - 1].append(val_c)
+ if val_d is not None:
+ val_d = int(round(val_d, 0))
+ diff[c - 1].append(val_d)
+
+ # Colorscales:
+ my_green = [[0.0, 'rgb(235, 249, 242)'],
+ [1.0, 'rgb(45, 134, 89)']]
+
+ my_blue = [[0.0, 'rgb(236, 242, 248)'],
+ [1.0, 'rgb(57, 115, 172)']]
+
+ my_grey = [[0.0, 'rgb(230, 230, 230)'],
+ [1.0, 'rgb(102, 102, 102)']]
+
+ hovertext = list()
+
+ annotations = list()
+ annotations_r = list()
+ annotations_c = list()
+ annotations_diff = list()
+
+ text = ("Test: {name}"
+ "
{title_r}: {text_r}"
+ "
{title_c}: {text_c}{text_diff}")
+ text_r = "Thput: {val_r}; StDev: {stdev_r}; Runs: {nr_r}"
+ text_c = "Thput: {val_c}; StDev: {stdev_c}; Runs: {nr_c}"
+ text_diff = "
Relative Difference {title_c} vs. {title_r}: {diff}%"
+
+ for c in range(len(txt_chains)):
+ hover_line = list()
+ for n in range(len(txt_nodes)):
+ point = dict(
+ x=n + 1,
+ y=c + 1,
+ xref="x",
+ yref="y",
+ xanchor="center",
+ yanchor="middle",
+ text="",
+ font=dict(
+ size=14,
+ ),
+ align="center",
+ showarrow=False
+ )
+
+ point_text_r = "Not present"
+ point_text_c = "Not present"
+ point_text_diff = ""
+ try:
+ point_r = data_r[c][n]
+ if point_r is not None:
+ point_text_r = text_r.format(
+ val_r=point_r,
+ stdev_r=vals[txt_chains[c]][txt_nodes[n]]["stdev_r"],
+ nr_r=vals[txt_chains[c]][txt_nodes[n]]["nr_r"])
+ except KeyError:
+ point_r = None
+ point["text"] = "" if point_r is None else point_r
+ annotations_r.append(deepcopy(point))
+
+ try:
+ point_c = data_c[c][n]
+ if point_c is not None:
+ point_text_c = text_c.format(
+ val_c=point_c,
+ stdev_c=vals[txt_chains[c]][txt_nodes[n]]["stdev_c"],
+ nr_c=vals[txt_chains[c]][txt_nodes[n]]["nr_c"])
+ except KeyError:
+ point_c = None
+ point["text"] = "" if point_c is None else point_c
+ annotations_c.append(deepcopy(point))
+
+ try:
+ point_d = diff[c][n]
+ if point_d is not None:
+ point_text_diff = text_diff.format(
+ title_r=plot["reference"]["name"],
+ title_c=plot["compare"]["name"],
+ diff=point_d)
+ except KeyError:
+ point_d = None
+ point["text"] = "" if point_d is None else point_d
+ annotations_diff.append(deepcopy(point))
+
+ try:
+ name = vals[txt_chains[c]][txt_nodes[n]]["name"]
+ except KeyError:
+ continue
+
+ hover_line.append(text.format(
+ name=name,
+ title_r=plot["reference"]["name"],
+ text_r=point_text_r,
+ title_c=plot["compare"]["name"],
+ text_c=point_text_c,
+ text_diff=point_text_diff
+ ))
+
+ hovertext.append(hover_line)
+
+ traces = [
+ plgo.Heatmap(x=nodes,
+ y=chains,
+ z=data_r,
+ visible=True,
+ colorbar=dict(
+ title=plot.get("z-axis", ""),
+ titleside="right",
+ titlefont=dict(
+ size=16
+ ),
+ tickfont=dict(
+ size=16,
+ ),
+ tickformat=".1f",
+ yanchor="bottom",
+ y=-0.02,
+ len=0.925,
+ ),
+ showscale=True,
+ colorscale=my_green,
+ reversescale=False,
+ text=hovertext,
+ hoverinfo="text"),
+ plgo.Heatmap(x=nodes,
+ y=chains,
+ z=data_c,
+ visible=False,
+ colorbar=dict(
+ title=plot.get("z-axis", ""),
+ titleside="right",
+ titlefont=dict(
+ size=16
+ ),
+ tickfont=dict(
+ size=16,
+ ),
+ tickformat=".1f",
+ yanchor="bottom",
+ y=-0.02,
+ len=0.925,
+ ),
+ showscale=True,
+ colorscale=my_blue,
+ reversescale=False,
+ text=hovertext,
+ hoverinfo="text"),
+ plgo.Heatmap(x=nodes,
+ y=chains,
+ z=diff,
+ name="Diff",
+ visible=False,
+ colorbar=dict(
+ title="Relative Difference {name_c} vs. {name_r} [%]".
+ format(name_c=plot["compare"]["name"],
+ name_r=plot["reference"]["name"]),
+ titleside="right",
+ titlefont=dict(
+ size=16
+ ),
+ tickfont=dict(
+ size=16,
+ ),
+ tickformat=".1f",
+ yanchor="bottom",
+ y=-0.02,
+ len=0.925,
+ ),
+ showscale=True,
+ colorscale=my_grey,
+ reversescale=False,
+ text=hovertext,
+ hoverinfo="text")
+ ]
+
+ for idx, item in enumerate(txt_nodes):
+ # X-axis, numbers:
+ annotations.append(dict(
+ x=idx+1,
+ y=0.05,
+ xref="x",
+ yref="y",
+ xanchor="center",
+ yanchor="top",
+ text=item,
+ font=dict(
+ size=16,
+ ),
+ align="center",
+ showarrow=False
+ ))
+ for idx, item in enumerate(txt_chains):
+ # Y-axis, numbers:
+ annotations.append(dict(
+ x=0.35,
+ y=idx+1,
+ xref="x",
+ yref="y",
+ xanchor="right",
+ yanchor="middle",
+ text=item,
+ font=dict(
+ size=16,
+ ),
+ align="center",
+ showarrow=False
+ ))
+ # X-axis, title:
+ annotations.append(dict(
+ x=0.55,
+ y=-0.15,
+ xref="paper",
+ yref="y",
+ xanchor="center",
+ yanchor="bottom",
+ text=plot.get("x-axis", ""),
+ font=dict(
+ size=16,
+ ),
+ align="center",
+ showarrow=False
+ ))
+ # Y-axis, title:
+ annotations.append(dict(
+ x=-0.1,
+ y=0.5,
+ xref="x",
+ yref="paper",
+ xanchor="center",
+ yanchor="middle",
+ text=plot.get("y-axis", ""),
+ font=dict(
+ size=16,
+ ),
+ align="center",
+ textangle=270,
+ showarrow=False
+ ))
+ updatemenus = list([
+ dict(
+ active=0,
+ x=1.0,
+ y=0.0,
+ xanchor='right',
+ yanchor='bottom',
+ direction='up',
+ buttons=list([
+ dict(
+ label=plot["reference"]["name"],
+ method="update",
+ args=[
+ {
+ "visible": [True, False, False]
+ },
+ {
+ "colorscale": [my_green, ],
+ "reversescale": False,
+ "annotations": annotations + annotations_r,
+ },
+ ]
+ ),
+ dict(
+ label=plot["compare"]["name"],
+ method="update",
+ args=[
+ {
+ "visible": [False, True, False]
+ },
+ {
+ "colorscale": [my_blue, ],
+ "reversescale": False,
+ "annotations": annotations + annotations_c,
+ },
+ ]
+ ),
+ dict(
+ label="Diff",
+ method="update",
+ args=[
+ {
+ "visible": [False, False, True]
+ },
+ {
+ "colorscale": [my_grey, ],
+ "reversescale": False,
+ "annotations": annotations + annotations_diff,
+ },
+ ]
+ ),
+ ])
+ )
+ ])
+
+ try:
+ layout = deepcopy(plot["layout"])
+ except KeyError as err:
+ logging.error("Finished with error: No layout defined")
+ logging.error(repr(err))
+ return
+
+ layout["annotations"] = annotations + annotations_r
+ layout['updatemenus'] = updatemenus
+
+ try:
+ # Create plot
+ plpl = plgo.Figure(data=traces, layout=layout)
+
+ # Export Plot
+ logging.info(" Writing file '{0}{1}'.".
+ format(plot["output-file"], plot["output-file-type"]))
+ ploff.plot(plpl, show_link=False, auto_open=False,
+ filename='{0}{1}'.format(plot["output-file"],
+ plot["output-file-type"]))
+ except PlotlyError as err:
+ logging.error(" Finished with error: {}".
+ format(str(err).replace("\n", " ")))
+ return