3 # Copyright (c) 2016 Cisco and/or its affiliates.
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at:
8 # http://www.apache.org/licenses/LICENSE-2.0
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
16 """Plot the performance data"""
24 import plotly.offline as ploff
25 import plotly.graph_objs as plgo
26 from lxml import etree
29 def select_files_in_subfolders(directory, ext='xml'):
30 """Get all files in folder and its subfolders.
32 :param dir: Input folder.
33 :param ext: File extension.
36 :return: List of filex matching the parameters.
39 for _, _, files in os.walk(directory):
41 if file.endswith('.%s' % ext):
42 yield os.path.join(directory, file)
45 def select_files_in_folder(directory, ext='xml'):
46 """Get all files in folder.
48 :param dir: Input folder.
49 :param ext: File extension.
52 :return: List of filex matching the parameters.
55 for file in os.listdir(directory):
56 if file.endswith('.%s' % ext):
57 yield os.path.join(directory, file)
60 def combine_dicts(first, second, oper=operator.add):
61 """Combine two dictionaries.
63 :param first: First dict.
64 :param second: Second dict.
65 :param oper: Operator.
69 :return: Combined dictionary.
73 return dict(first.items() + second.items() +\
74 [(k, oper(first[k], second[k])) for k in set(second) & set(first)])
77 def parse_data_pps(args):
78 """Get PPS data out of XML file into array.
80 :param args: Command line parameters.
81 :type suite: ArgumentParser
82 :return: X-data and Y-data dictionaries.
88 for i, file in enumerate(sorted(select_files_in_folder(args.input))):
89 xml_tree = etree.parse(file)
90 sel = xml_tree.xpath(args.xpath)
92 ydata_pps = combine_dicts(ydata_pps, dict((elem.attrib['name'],\
93 (i, float(elem.text))) for elem in sel))
94 xdata.append(xml_tree.getroot().attrib['vdevice'])
95 return xdata, ydata_pps
98 def parse_data_lat(args):
99 """Get latency data out of XML file into array.
101 :param args: Command line parameters.
102 :type suite: ArgumentParser
103 :return: X-data and Y-data dictionaries.
109 for i, file in enumerate(sorted(select_files_in_folder(args.input))):
110 xml_tree = etree.parse(file)
111 sel = xml_tree.xpath(args.xpath)
114 ydata_lat = combine_dicts(ydata_lat, dict((elem.attrib['name'],\
115 (i, elem.attrib[args.latency])) for elem in sel))
117 raise RuntimeError('Retrieving latency data error (PDR?)')
118 xdata.append(xml_tree.getroot().attrib['vdevice'])
119 return xdata, ydata_lat
123 """Parse arguments from cmd line.
125 :return: Parsed arguments.
126 :rtype ArgumentParser
129 parser = argparse.ArgumentParser()
130 parser.add_argument("-x", "--xpath", required=True,
132 parser.add_argument("-t", "--title", required=True,
134 parser.add_argument("-l", "--lower",
136 help="Lower boudary of Y-axis")
137 parser.add_argument("-u", "--upper",
139 help="Upper boudary of Y-axis")
140 parser.add_argument("-e", "--errorbar",
142 help="Errorbar for Y-axis")
143 parser.add_argument("-d", "--latency",
144 choices=['lat_10', 'lat_50', 'lat_100'],
145 help="Latency to draw")
146 parser.add_argument("-p", "--plot",
147 choices=['box', 'scatter'],
149 help="Throughput plot type")
150 parser.add_argument("-i", "--input",
152 parser.add_argument("-o", "--output", required=True,
153 help="Output image file name")
154 return parser.parse_args()
162 xdata, ydata = parse_data_lat(args)
164 xdata, ydata = parse_data_pps(args)
166 # Print data into console for debug
169 print data + ";" + ";".join(str(val) for val in ydata[data][1::2])
174 for i, suite in enumerate(ydata):
177 _ = [y_extract.extend([l, l]) for l in ydata[suite][1::2][0].split('/')]
178 traces.append(plgo.Box(
179 x=['TGint1-to-SUT1-to-SUT2-to-TGint2',
180 'TGint1-to-SUT1-to-SUT2-to-TGint2',
181 'TGint1-to-SUT1-to-SUT2-to-TGint2',
182 'TGint1-to-SUT1-to-SUT2-to-TGint2',
183 'TGint1-to-SUT1-to-SUT2-to-TGint2',
184 'TGint1-to-SUT1-to-SUT2-to-TGint2',
185 'TGint2-to-SUT2-to-SUT1-to-TGint1',
186 'TGint2-to-SUT2-to-SUT1-to-TGint1',
187 'TGint2-to-SUT2-to-SUT1-to-TGint1',
188 'TGint2-to-SUT2-to-SUT1-to-TGint1',
189 'TGint2-to-SUT2-to-SUT1-to-TGint1',
190 'TGint2-to-SUT2-to-SUT1-to-TGint1'],
192 name=str(i+1)+'. '+suite.lower().replace('-ndrdisc',''),
196 if args.plot == 'box':
197 traces.append(plgo.Box(
198 x=[str(i+1)+'.'] * len(ydata[suite][1::2]),
199 y=ydata[suite][1::2],
200 name=str(i+1)+'. '+suite.lower().replace('-ndrdisc',''),
202 boxpoints='outliers',
205 elif args.plot == 'scatter':
206 traces.append(plgo.Scatter(
207 x=ydata[suite][0::2],
208 y=ydata[suite][1::2],
209 mode='lines+markers',
210 name=str(i+1)+'. '+suite.lower().replace('-ndrdisc',''),
216 layout = plgo.Layout(
217 title='{0}'.format(args.title),
222 gridcolor='rgb(238, 238, 238)',
223 linecolor='rgb(238, 238, 238)',
228 tickcolor='rgb(238, 238, 238)',
230 title='Indexed Test Cases' if args.plot == 'box'\
235 gridcolor='rgb(238, 238, 238)',
236 hoverformat='' if args.latency else '.4s',
237 linecolor='rgb(238, 238, 238)',
239 range=[args.lower, args.upper] if args.lower and args.upper\
244 tickcolor='rgb(238, 238, 238)',
245 title='Latency min/avg/max [uSec]' if args.latency\
246 else 'Packets Per Second [pps]',
266 plpl = plgo.Figure(data=traces, layout=layout)
269 show_link=False, auto_open=False,
270 filename='{0}.html'.format(args.output))
272 sys.stderr.write('No data found!\n')
275 if __name__ == "__main__":