# 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 = ''' %s
''' 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 + \ '
' + \ '
\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('Generating chart drawing code 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='') 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='') 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='') opts = parser.parse_args() if opts.version: print('Version ' + __version__) sys.exit(0) gen_chart(opts.files, opts.chart, opts.browser, opts.protocols)