Add generation of chart from json

Change-Id: I08bb5d6a5f6d6348e63f93cbf5c2c05484ebf9b9
This commit is contained in:
ahothan 2015-04-27 12:57:38 -07:00
parent 1bfd2b3faa
commit 07c0195c2c

273
genchart.py Executable file
View 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)