CSIT 1701 report files and script
[csit.git] / resources / tools / report_gen / run_plot.py
1 #!/usr/bin/python
2
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:
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
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.
15
16 """Plot the performance data"""
17
18 import argparse
19 import operator
20 import os
21 import sys
22 import math
23
24 import plotly.offline as ploff
25 import plotly.graph_objs as plgo
26 from lxml import etree
27
28
29 def select_files_in_subfolders(directory, ext='xml'):
30     """Get all files in folder and its subfolders.
31
32     :param dir: Input folder.
33     :param ext: File extension.
34     :type dir: str
35     :type ext: str
36     :return: List of filex matching the parameters.
37     :rtype list
38     """
39     for _, _, files in os.walk(directory):
40         for file in files:
41             if file.endswith('.%s' % ext):
42                 yield os.path.join(directory, file)
43
44
45 def select_files_in_folder(directory, ext='xml'):
46     """Get all files in folder.
47
48     :param dir: Input folder.
49     :param ext: File extension.
50     :type dir: str
51     :type ext: str
52     :return: List of filex matching the parameters.
53     :rtype list
54     """
55     for file in os.listdir(directory):
56         if file.endswith('.%s' % ext):
57             yield os.path.join(directory, file)
58
59
60 def combine_dicts(first, second, oper=operator.add):
61     """Combine two dictionaries.
62
63     :param first: First dict.
64     :param second: Second dict.
65     :param oper: Operator.
66     :type first: dict
67     :type second: dict
68     :type oper: operator
69     :return: Combined dictionary.
70     :rtype dict
71     """
72
73     return dict(first.items() + second.items() +\
74         [(k, oper(first[k], second[k])) for k in set(second) & set(first)])
75
76
77 def parse_data_pps(args):
78     """Get PPS data out of XML file into array.
79
80     :param args: Command line parameters.
81     :type suite: ArgumentParser
82     :return: X-data and Y-data dictionaries.
83     :rtype tuple of dict
84     """
85     xdata = []
86     ydata_pps = {}
87
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)
91         if sel:
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
96
97
98 def parse_data_lat(args):
99     """Get latency data out of XML file into array.
100
101     :param args: Command line parameters.
102     :type suite: ArgumentParser
103     :return: X-data and Y-data dictionaries.
104     :rtype tuple of dict
105     """
106     xdata = []
107     ydata_lat = {}
108
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)
112         if sel:
113             try:
114                 ydata_lat = combine_dicts(ydata_lat, dict((elem.attrib['name'],\
115                     (i, elem.attrib[args.latency])) for elem in sel))
116             except KeyError:
117                 raise RuntimeError('Retrieving latency data error (PDR?)')
118             xdata.append(xml_tree.getroot().attrib['vdevice'])
119     return xdata, ydata_lat
120
121
122 def parse_args():
123     """Parse arguments from cmd line.
124
125     :return: Parsed arguments.
126     :rtype ArgumentParser
127     """
128
129     parser = argparse.ArgumentParser()
130     parser.add_argument("-x", "--xpath", required=True,
131                         help="Xpath filter")
132     parser.add_argument("-t", "--title", required=True,
133                         help="Plot title")
134     parser.add_argument("-l", "--lower",
135                         default=False,
136                         help="Lower boudary of Y-axis")
137     parser.add_argument("-u", "--upper",
138                         default=False,
139                         help="Upper boudary of Y-axis")
140     parser.add_argument("-e", "--errorbar",
141                         default=False,
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("-i", "--input",
147                         help="Input folder")
148     parser.add_argument("-o", "--output", required=True,
149                         help="Output image file name")
150     return parser.parse_args()
151
152
153 def main():
154     """Main function."""
155
156     args = parse_args()
157     if args.latency:
158         xdata, ydata = parse_data_lat(args)
159     else:
160         xdata, ydata = parse_data_pps(args)
161
162     # Print data into console for debug
163     row_format = "{:>60}" * (len(xdata) + 1)
164     print row_format.format(args.title, *xdata)
165     for data in ydata:
166         try:
167             print row_format.format(data, *ydata[data][1::2])
168         except IndexError:
169             print "{:>60}".format(data), ydata[data]
170
171     if xdata and ydata:
172         traces = []
173         # Add plot traces
174         for i, suite in enumerate(ydata):
175             if args.latency:
176                 y_extract = []
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'],
191                     y=y_extract,
192                     name=str(i+1)+'. '+suite.lower().replace('-ndrdisc',''),
193                     boxmean=False,
194                 ))
195             else:
196                 traces.append(plgo.Scatter(
197                     x=ydata[suite][0::2],
198                     y=ydata[suite][1::2],
199                     mode='lines+markers',
200                     name=str(i+1)+'. '+suite.lower().replace('-ndrdisc',''),
201                 ))
202
203         # Add plot layout
204         layout = plgo.Layout(
205             title='{0}'.format(args.title),
206             xaxis=dict(
207                 autorange=True,
208                 autotick=False,
209                 fixedrange=False,
210                 gridcolor='rgb(238, 238, 238)',
211                 linecolor='rgb(238, 238, 238)',
212                 linewidth=1,
213                 showgrid=True,
214                 showline=True,
215                 showticklabels=True,
216                 tickcolor='rgb(238, 238, 238)',
217                 tickmode='linear',
218                 zeroline=False,
219             ),
220             yaxis=dict(
221                 gridcolor='rgb(238, 238, 238)',
222                 hoverformat='' if args.latency else '.4s',
223                 linecolor='rgb(238, 238, 238)',
224                 linewidth=1,
225                 range=[args.lower, args.upper],
226                 showgrid=True,
227                 showline=True,
228                 showticklabels=True,
229                 tickcolor='rgb(238, 238, 238)',
230                 title='Latency min/avg/max [uSec]' if args.latency\
231                     else 'Packets Per Second [pps]',
232                 zeroline=False,
233             ),
234             boxmode='group',
235             boxgroupgap=0.5,
236             autosize=False,
237             margin=dict(
238                 autoexpand=False,
239                 b=200,
240                 l=50,
241                 r=50,
242             ),
243             showlegend=True,
244             legend=dict(
245                 orientation='h',
246             ),
247             width=700,
248             height=700,
249         )
250         # Create plot
251         plpl = plgo.Figure(data=traces, layout=layout)
252         # Export Plot
253         ploff.plot(plpl,
254                    show_link=False, auto_open=False,
255                    filename='{0}.html'.format(args.output))
256     else:
257         sys.stderr.write('No data found!\n')
258
259
260 if __name__ == "__main__":
261     sys.exit(main())