Add generation of chart from json
Change-Id: I08bb5d6a5f6d6348e63f93cbf5c2c05484ebf9b9
This commit is contained in:
parent
1bfd2b3faa
commit
07c0195c2c
273
genchart.py
Executable file
273
genchart.py
Executable file
@ -0,0 +1,273 @@
|
||||
# Copyright 2014 Cisco Systems, Inc. All rights reserved.
|
||||
#
|
||||
# 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
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
|
||||
# This is an example of tool that can represent VMTP json results in
|
||||
# a nicer form using HTML and the Google Charts Javascript library
|
||||
#
|
||||
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import os.path
|
||||
import sys
|
||||
import webbrowser
|
||||
|
||||
__version__ = '0.0.1'
|
||||
|
||||
html_main_template = '''
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="https://www.google.com/jsapi"></script>
|
||||
<script type="text/javascript">
|
||||
google.load("visualization", "1.1", {packages:["bar", "table"]});
|
||||
google.setOnLoadCallback(drawChart);
|
||||
function drawChart() {
|
||||
var options;
|
||||
var data;
|
||||
var chart;
|
||||
%s
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<table align="center">
|
||||
%s
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
'''
|
||||
|
||||
js_options_tpl = '''
|
||||
var options = {
|
||||
chart: {
|
||||
title: 'OpenStack Data Plane Performance (Gbps)',
|
||||
subtitle: '%s',
|
||||
}
|
||||
};
|
||||
'''
|
||||
|
||||
js_data_tpl = '''
|
||||
data = google.visualization.arrayToDataTable([
|
||||
%s
|
||||
]);
|
||||
chart = new %s(document.getElementById('vmtp-%s%d'));
|
||||
chart.draw(data, options);
|
||||
'''
|
||||
|
||||
div_tpl = ' ' * 6 + \
|
||||
'<tr><td><div id="vmtp-%s%d" style="width:900px;height:500px;">' + \
|
||||
'</div></td></tr>\n'
|
||||
|
||||
# must be exact match
|
||||
label_match = {
|
||||
'VM to VM same network fixed IP (inter-node)': 'L2',
|
||||
'VM to VM different network fixed IP (inter-node)': 'L3 fixed',
|
||||
'VM to VM different network floating IP (inter-node)': 'L3 floating',
|
||||
# This one is special because it is bidirectional
|
||||
'External-VM': ''
|
||||
}
|
||||
|
||||
prop_match = {
|
||||
'cpu_info': 'CPU Info',
|
||||
'distro': 'Host Linux distribution',
|
||||
'date': 'Date',
|
||||
'nic_name': 'NIC name',
|
||||
'openstack_version': 'OpenStack release',
|
||||
'test_description': 'Description',
|
||||
'version': 'VMTP version',
|
||||
'encapsulation': 'Encapsulation',
|
||||
'l2agent_type': 'L2 agent type'
|
||||
}
|
||||
|
||||
# what goes in the subtitle
|
||||
subtitle_match = ['test_description', 'openstack_version', 'distro',
|
||||
'encapsulation', 'l2agent_type']
|
||||
|
||||
class GoogleChartsBarChart:
|
||||
def __init__(self, results, protocols):
|
||||
self.results = results
|
||||
if protocols not in ['udp', 'tcp']:
|
||||
protocols = 'all'
|
||||
self.show_udp = protocols in ['all', 'udp']
|
||||
self.show_tcp = protocols in ['all', 'tcp']
|
||||
|
||||
def _get_subtitle(self, res):
|
||||
sub = 'inter-node'
|
||||
for key in subtitle_match:
|
||||
if key in res:
|
||||
sub += ' ' + res[key].encode('ascii')
|
||||
return sub
|
||||
|
||||
def _get_categories(self, flow):
|
||||
categories = ['Flow']
|
||||
# start with UDP first
|
||||
if self.show_udp:
|
||||
# iterate through all results in this flow to pick the sizes
|
||||
for flow_res in flow['results']:
|
||||
if flow_res['protocol'] == 'UDP':
|
||||
categories.append('UDP ' + str(flow_res['pkt_size']))
|
||||
if self.show_tcp:
|
||||
categories.append('TCP')
|
||||
return categories
|
||||
|
||||
def _get_flow_data(self, label, flow, reverse=False):
|
||||
data = [label]
|
||||
# start with UDP first
|
||||
if self.show_udp:
|
||||
# iterate through all results in this flow to pick the sizes
|
||||
for flow_res in flow['results']:
|
||||
reverse_flow = 'direction' in flow_res
|
||||
if reverse_flow != reverse:
|
||||
continue
|
||||
if flow_res['protocol'] == 'UDP':
|
||||
data.append(float(flow_res['throughput_kbps']) / (1024 * 1024))
|
||||
if self.show_tcp:
|
||||
# TCP may have multiple samples - pick the average for now
|
||||
res = []
|
||||
for flow_res in flow['results']:
|
||||
if reverse and 'direction' not in flow_res:
|
||||
continue
|
||||
if flow_res['protocol'] == 'TCP':
|
||||
res.append(float(flow_res['throughput_kbps']) / (1024 * 1024))
|
||||
break
|
||||
if res:
|
||||
total_tp = 0
|
||||
for tp in res:
|
||||
total_tp += tp
|
||||
data.append(total_tp / len(res))
|
||||
return data
|
||||
|
||||
def _get_flows(self, flows):
|
||||
res = []
|
||||
for flow in flows:
|
||||
desc = flow['desc']
|
||||
if desc in label_match:
|
||||
if label_match[desc]:
|
||||
res.append(self._get_flow_data(label_match[desc], flow))
|
||||
else:
|
||||
# upload/download
|
||||
res.append(self._get_flow_data('Upload', flow, reverse=False))
|
||||
res.append(self._get_flow_data('Download', flow, reverse=True))
|
||||
return res
|
||||
|
||||
def _get_js_options(self, res):
|
||||
subtitle = self._get_subtitle(res)
|
||||
return js_options_tpl % (subtitle)
|
||||
|
||||
def _get_js_chart(self, chart_class, rows, chart_name, id):
|
||||
data = ''
|
||||
for row in rows:
|
||||
data += ' ' * 12 + str(row) + ',\n'
|
||||
return js_data_tpl % (data, chart_class, chart_name, id)
|
||||
|
||||
def _get_js_data(self, flows, id):
|
||||
rows = [self._get_categories(flows[0])]
|
||||
rows.extend(self._get_flows(flows))
|
||||
return self._get_js_chart('google.charts.Bar', rows, 'chart', id)
|
||||
|
||||
def _get_js_props(self, res, id):
|
||||
rows = [['Property', 'Value']]
|
||||
for key in prop_match:
|
||||
if key in res:
|
||||
rows.append([prop_match[key], res[key].encode('ascii', 'ignore')])
|
||||
return self._get_js_chart('google.visualization.Table', rows, 'table', id)
|
||||
|
||||
def _get_js(self, res, id):
|
||||
js = ''
|
||||
js += self._get_js_options(res)
|
||||
js += self._get_js_data(res['flows'], id)
|
||||
# Add property table
|
||||
js += self._get_js_props(res, id)
|
||||
return js
|
||||
|
||||
def _get_jss(self):
|
||||
js = ''
|
||||
id = 0
|
||||
for res in self.results:
|
||||
js += self._get_js(res, id)
|
||||
id += 1
|
||||
return js
|
||||
|
||||
def _get_divs(self):
|
||||
divs = ''
|
||||
id = 0
|
||||
for _ in self.results:
|
||||
divs += div_tpl % ('chart', id)
|
||||
divs += div_tpl % ('table', id)
|
||||
id += 1
|
||||
return divs
|
||||
|
||||
def _plot(self, dest):
|
||||
dest.write(html_main_template % (self._get_jss(), self._get_divs()))
|
||||
|
||||
def plot(self, dest_file):
|
||||
with open(dest_file, 'w') as dest:
|
||||
print('Plotting bar chart to ' + dest_file + '...')
|
||||
self._plot(dest)
|
||||
|
||||
def gen_chart(files, chart_dest, browser, protocols=''):
|
||||
results = []
|
||||
for ff in files:
|
||||
if not os.path.isfile(ff):
|
||||
print('Error: No such file %s: ' + ff)
|
||||
sys.exit(1)
|
||||
with open(ff) as data_file:
|
||||
res = json.load(data_file)
|
||||
results.append(res)
|
||||
|
||||
chart = GoogleChartsBarChart(results, protocols.lower())
|
||||
chart.plot(chart_dest)
|
||||
if browser:
|
||||
url = 'file://' + os.path.abspath(opts.chart)
|
||||
webbrowser.open(url, new=2)
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
parser = argparse.ArgumentParser(description='VMTP Chart Generator V' + __version__)
|
||||
|
||||
parser.add_argument('-c', '--chart', dest='chart',
|
||||
action='store',
|
||||
help='create and save chart in html file',
|
||||
metavar='<file>')
|
||||
|
||||
parser.add_argument('-b', '--browser', dest='browser',
|
||||
action='store_true',
|
||||
default=False,
|
||||
help='display (-c) chart in the browser')
|
||||
|
||||
parser.add_argument('-p', '--protocol', dest='protocols',
|
||||
action='store',
|
||||
default='all',
|
||||
help='select protocols:all, tcp, udp',
|
||||
metavar='<all|tcp|udp>')
|
||||
|
||||
parser.add_argument('-v', '--version', dest='version',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='print version of this script and exit')
|
||||
|
||||
parser.add_argument(dest='files',
|
||||
help='vmtp json result file', nargs='+',
|
||||
metavar='<file>')
|
||||
|
||||
opts = parser.parse_args()
|
||||
|
||||
if opts.version:
|
||||
print('Version ' + __version__)
|
||||
sys.exit(0)
|
||||
|
||||
gen_chart(opts.files, opts.chart, opts.browser, opts.protocols)
|
Loading…
Reference in New Issue
Block a user