Change the result JSON schema to include metadata
1. Change the result JSON schema to include metadata; 2. Automatic adjust the timeout for initializing volumes; 3. Remove the old kb_gen_chart.py script; 4. Doc update for storage test on ephemeral disks; Change-Id: Iac9e910efb722e949f4fe1c8d8f100a54dfc4ea5
This commit is contained in:
parent
3727636ac0
commit
8ca72c93f4
@ -314,14 +314,19 @@ This section controls how the Storage tests will be performed. All the fields
|
||||
are self-explained, and you can create your own test case with customized
|
||||
parameters.
|
||||
|
||||
* **client:volume_size**
|
||||
* **client:storage_target**
|
||||
|
||||
This controls the size of the Cinder volume to be attached to each VM instance.
|
||||
(in GB)
|
||||
KloudBuster supports to test the storage performance on Cinder volumes or
|
||||
ephemeral disks. Specify the testing target here.
|
||||
|
||||
* **client:disk_size**
|
||||
|
||||
This controls the size of the Cinder volume or ephemeral disk to be attached to
|
||||
each VM instance. (in GB)
|
||||
|
||||
* **client:io_file_size**
|
||||
|
||||
This controls the size of the test file to be used for storage testing. (in GiB)
|
||||
This controls the size of the test file to be used for storage testing. (in GB)
|
||||
|
||||
|
||||
Advanced Features
|
||||
|
@ -1,227 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
# 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.
|
||||
#
|
||||
|
||||
# A tool that can represent KloudBuster json results in
|
||||
# a nicer form using HTML5, bootstrap.js and the Google Charts Javascript library
|
||||
#
|
||||
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
import webbrowser
|
||||
|
||||
from jinja2 import Environment
|
||||
from jinja2 import FileSystemLoader
|
||||
|
||||
__version__ = '0.0.1'
|
||||
kb_html_tpl = "./kb_tpl.jinja"
|
||||
|
||||
def get_formatted_num(value):
|
||||
return '{:,}'.format(value)
|
||||
|
||||
# table column names
|
||||
col_names = ['<i class="glyphicon glyphicon-file"></i> Description',
|
||||
'<i class="glyphicon glyphicon-random"></i> Connections',
|
||||
'<i class="glyphicon glyphicon-book"></i> Server VMs',
|
||||
'<i class="glyphicon glyphicon-transfer"></i> Requests',
|
||||
'<i class="glyphicon glyphicon-fire"></i> Socket errors',
|
||||
'<i class="glyphicon glyphicon-time"></i> RPS measured',
|
||||
'<i class="glyphicon glyphicon-pencil"></i> RPS requested',
|
||||
'<i class="glyphicon glyphicon-cloud-download"></i> RX throughput (Gbps)']
|
||||
|
||||
def get_new_latency_tuples():
|
||||
'''Returns a list of lists initializedas follows
|
||||
The latency tuples must be formatted like this:
|
||||
['Percentile', 'fileA', 'fileB'],
|
||||
[2.0, 1, 3],
|
||||
[4.0, 27, 38],
|
||||
etc, with the first column being calculated from the percentile
|
||||
using the formula (1/(1-percentile))
|
||||
50% -> 1/0.5 = 2
|
||||
75% -> 4
|
||||
etc...
|
||||
This horizontal scaling is used to stretch the chart at the top end
|
||||
(towards 100%)
|
||||
'''
|
||||
return [
|
||||
['Percentile'], # add run file name
|
||||
[2], # add run 50% latency
|
||||
[4], # add run 75% latency
|
||||
[10],
|
||||
[100],
|
||||
[1000],
|
||||
[10000],
|
||||
[100000] # add run 99.999% latency
|
||||
]
|
||||
|
||||
class KbReport(object):
|
||||
def __init__(self, data_list, line_rate):
|
||||
self.data_list = data_list
|
||||
self.latency_tuples = get_new_latency_tuples()
|
||||
self.common_stats = []
|
||||
self.table = None
|
||||
template_loader = FileSystemLoader(searchpath=".")
|
||||
template_env = Environment(loader=template_loader)
|
||||
self.tpl = template_env.get_template(kb_html_tpl)
|
||||
self.line_rate = line_rate
|
||||
|
||||
def add_latency_stats(self, run_results):
|
||||
# init a column list
|
||||
column = [run_results['description']]
|
||||
for latency_pair in run_results['latency_stats']:
|
||||
# convert from usec to msec
|
||||
latency_ms = latency_pair[1] / 1000
|
||||
column.append(latency_ms)
|
||||
# and append that column to the latency list
|
||||
for pct_list, colval in zip(self.latency_tuples, column):
|
||||
pct_list.append(colval)
|
||||
|
||||
def prepare_table(self):
|
||||
table = {}
|
||||
table['col_names'] = col_names
|
||||
# add values for each row
|
||||
rows = []
|
||||
for run_res in self.data_list:
|
||||
rps_max = run_res['http_rate_limit']
|
||||
rx_tp = float(run_res['http_throughput_kbytes'])
|
||||
rx_tp = round(rx_tp * 8 / (1024 * 1024), 1)
|
||||
cells = [run_res['description'],
|
||||
get_formatted_num(run_res['total_connections']),
|
||||
get_formatted_num(run_res['total_server_vms']),
|
||||
get_formatted_num(run_res['http_total_req']),
|
||||
get_formatted_num(run_res['http_sock_err'] + run_res['http_sock_timeout']),
|
||||
get_formatted_num(run_res['http_rps']),
|
||||
get_formatted_num(rps_max)]
|
||||
row = {'cells': cells,
|
||||
'rx': {'value': rx_tp,
|
||||
'max': self.line_rate,
|
||||
'percent': (rx_tp * 100) / self.line_rate}}
|
||||
rows.append(row)
|
||||
table['rows'] = rows
|
||||
self.table = table
|
||||
|
||||
def plot(self, dest_file):
|
||||
for run_results in self.data_list:
|
||||
self.add_latency_stats(run_results)
|
||||
|
||||
self.prepare_table()
|
||||
kbstats = {
|
||||
'table': self.table,
|
||||
'latency_tuples': self.latency_tuples,
|
||||
'search_page': 'true' if len(self.data_list) > 10 else 'false'
|
||||
}
|
||||
with open(dest_file, 'w') as dest:
|
||||
print('Generating chart drawing code to ' + dest_file + '...')
|
||||
output = self.tpl.render(kbstats=kbstats)
|
||||
dest.write(output)
|
||||
|
||||
def get_display_file_name(filename):
|
||||
res = os.path.basename(filename)
|
||||
# remove extension
|
||||
res, _ = os.path.splitext(res)
|
||||
return res
|
||||
|
||||
def guess_line_rate(data_list):
|
||||
max_tp_kb = 0
|
||||
for data_list in data_list:
|
||||
max_tp_kb = max(max_tp_kb, data_list['http_throughput_kbytes'])
|
||||
max_tp_gb = (max_tp_kb * 8) / (1000 * 1000)
|
||||
# typical GE line rates are 10, 40 and 100
|
||||
if max_tp_gb < 10:
|
||||
return 10
|
||||
if max_tp_gb < 40:
|
||||
return 40
|
||||
return 100
|
||||
|
||||
def gen_chart(file_list, chart_dest, browser, line_rate):
|
||||
|
||||
data_list = []
|
||||
for res_file in file_list:
|
||||
print 'processing: ' + res_file
|
||||
if not os.path.isfile(res_file):
|
||||
print('Error: No such file %s: ' + res_file)
|
||||
sys.exit(1)
|
||||
with open(res_file) as data_file:
|
||||
results = json.load(data_file)
|
||||
for run in results:
|
||||
des_str = get_display_file_name(res_file)
|
||||
if 'description' in run:
|
||||
stage = re.search(r'Stage (\d+)\:', run['description']).group(1)
|
||||
des_str += ': Stage ' + stage
|
||||
run['description'] = str(des_str)
|
||||
data_list.append(run)
|
||||
if not line_rate:
|
||||
line_rate = guess_line_rate(data_list)
|
||||
print 'Guessed line rate: %s Gbps.' % line_rate
|
||||
chart = KbReport(data_list, line_rate)
|
||||
print('Generating report to ' + chart_dest + '...')
|
||||
chart.plot(chart_dest)
|
||||
if browser:
|
||||
url = 'file://' + os.path.abspath(chart_dest)
|
||||
webbrowser.open(url, new=2)
|
||||
|
||||
def get_absolute_path_for_file(file_name):
|
||||
'''
|
||||
Return the filename in absolute path for any file
|
||||
passed as relateive path.
|
||||
'''
|
||||
abs_file = os.path.dirname(os.path.abspath(__file__))
|
||||
return abs_file + '/' + file_name
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='KloudBuster Chart Generator V' + __version__)
|
||||
|
||||
parser.add_argument('-c', '--chart', dest='chart',
|
||||
action='store', required=True,
|
||||
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('-v', '--version', dest='version',
|
||||
default=False,
|
||||
action='store_true',
|
||||
help='print version of this script and exit')
|
||||
|
||||
parser.add_argument('-l', '--line-rate', dest='line_rate',
|
||||
action='store',
|
||||
default=0,
|
||||
type=int,
|
||||
help='line rate in Gbps (default=10)',
|
||||
metavar='<rate-Gbps>')
|
||||
|
||||
parser.add_argument(dest='files',
|
||||
help='KloudBuster 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.line_rate)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -70,7 +70,9 @@ class KBRunner_Storage(KBRunner):
|
||||
msg = "Stage %d: %d VM(s)%s%s" % (stage, vm_count, iops_str, tp_str)
|
||||
return msg
|
||||
|
||||
def init_volume(self, active_range, timeout=30):
|
||||
def init_volume(self, active_range):
|
||||
# timeout is calculated as 30s/GB
|
||||
timeout = 30 * self.config.io_file_size
|
||||
parameter = {'size': str(self.config.io_file_size) + 'GiB'}
|
||||
parameter['mkfs'] = True if self.config.storage_target == 'volume' else False
|
||||
|
||||
@ -102,7 +104,7 @@ class KBRunner_Storage(KBRunner):
|
||||
if self.config.storage_target == 'volume':
|
||||
LOG.info("Initializing volume and setting up filesystem...")
|
||||
else:
|
||||
LOG.info("Initializing ephermeral disk...")
|
||||
LOG.info("Initializing ephemeral disk...")
|
||||
self.init_volume(active_range)
|
||||
|
||||
if self.config.prompt_before_run:
|
||||
|
@ -1,129 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="http://code.jquery.com/jquery.min.js"></script>
|
||||
<link href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
|
||||
<script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/js/bootstrap.min.js"></script>
|
||||
<script src="http://cdn.datatables.net/1.10.7/js/jquery.dataTables.min.js"></script>
|
||||
<link href="http://cdn.datatables.net/1.10.7/css/jquery.dataTables.min.css" rel="stylesheet" type="text/css" />
|
||||
<style media="all" type="text/css">
|
||||
.alignRight { text-align: right; }
|
||||
</style>
|
||||
<meta charset="utf-8">
|
||||
<script type="text/javascript"
|
||||
src="https://www.google.com/jsapi?autoload={
|
||||
'modules':[{
|
||||
'name':'visualization',
|
||||
'version':'1',
|
||||
'packages':['corechart']
|
||||
}]
|
||||
}"></script>
|
||||
<script type="text/javascript">
|
||||
google.setOnLoadCallback(drawChart);
|
||||
|
||||
function drawChart() {
|
||||
var data = google.visualization.arrayToDataTable(
|
||||
{{kbstats.latency_tuples}}
|
||||
);
|
||||
var ticks =
|
||||
[{v:2,f:'50%'},
|
||||
{v:4,f:'75%'},
|
||||
{v:10,f:'90%'},
|
||||
{v:100,f:'99%'},
|
||||
{v:1000,f:'99.9%'},
|
||||
{v:10000,f:'99.99%'},
|
||||
{v:100000,f:'99.999%'},
|
||||
];
|
||||
var options = {
|
||||
title: 'HTTP Requests Latency Distribution',
|
||||
curveType: 'function',
|
||||
hAxis: {title: 'Percentile', minValue: 0, logScale: true, ticks:ticks },
|
||||
vAxis: {title: 'Latency (ms)', minValue: 0, logScale: true,
|
||||
gridlines: {count: 8},
|
||||
minorGridlines: {count: 1},
|
||||
minValue: 0 },
|
||||
legend: { position: 'bottom' }
|
||||
};
|
||||
|
||||
var chart = new google.visualization.LineChart(document.getElementById('curve_chart'));
|
||||
// add tooptips with correct percentile text to data:
|
||||
var columns = [0];
|
||||
for (var i = 1; i < data.getNumberOfColumns(); i++) {
|
||||
columns.push(i);
|
||||
columns.push({
|
||||
type: 'string',
|
||||
properties: {
|
||||
role: 'tooltip'
|
||||
},
|
||||
calc: (function (j) {
|
||||
return function (dt, row) {
|
||||
var percentile = 100.0 - (100.0/dt.getValue(row, 0));
|
||||
return dt.getColumnLabel(j) + ': ' +
|
||||
percentile +
|
||||
'\%\'ile = ' + dt.getValue(row, j) + ' msec'
|
||||
}
|
||||
})(i)
|
||||
});
|
||||
}
|
||||
var view = new google.visualization.DataView(data);
|
||||
view.setColumns(columns);
|
||||
chart.draw(view, options);
|
||||
}
|
||||
// For jquery dataTable
|
||||
$(document).ready(function() {
|
||||
$('#runs_table').dataTable({
|
||||
"searching": {{kbstats.search_page}},
|
||||
"paging": {{kbstats.search_page}},
|
||||
"bInfo" : {{kbstats.search_page}},
|
||||
"aoColumnDefs": [
|
||||
{ "sClass": "alignRight", "aTargets": [ 1, 2, 3, 4, 5, 6 ] }
|
||||
]
|
||||
});
|
||||
} );
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container-fluid">
|
||||
<h2><i class="glyphicon glyphicon-dashboard"></i> KloudBuster Report</h2>
|
||||
<div class="panel panel-primary">
|
||||
<div class="panel-heading"><h3>HTTP Scale Results</h3></div>
|
||||
<div class="panel-body">
|
||||
|
||||
{% if kbstats.table %}
|
||||
<div class="row"><!-- ROW1 -->
|
||||
<table id="runs_table" class="table hover display compact" cellspacing="0" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
{% for col_name in kbstats.table.col_names %}
|
||||
<th>{{col_name}}</th>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody>
|
||||
{% for row in kbstats.table.rows %}
|
||||
<tr>
|
||||
{% for cell in row.cells %}
|
||||
<td>{{cell}}</td>
|
||||
{% endfor %}
|
||||
<td>
|
||||
<div class="progress-bar" role="progressbar" style="width:{{row.rx.percent}}%;min-width: 20px">
|
||||
<span>{{ row.rx.value }}</span>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div><!-- ROW1 -->
|
||||
{% endif %}
|
||||
|
||||
|
||||
<div class="row" align="center"><!-- LATENCY CHART ROW -->
|
||||
<div id="curve_chart" style="width: 900px; height: 500px"></div>
|
||||
</div><!-- LATENCY CHART ROW -->
|
||||
</div><!--/panel-body-->
|
||||
</div><!--/panel-->
|
||||
</div><!--/container-->
|
||||
</body>
|
||||
</html>
|
@ -14,6 +14,7 @@
|
||||
# under the License.
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
import datetime
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
@ -257,7 +258,7 @@ class KloudBuster(object):
|
||||
LOG.info('Automatically setting "use_floatingip" to True for server cloud...')
|
||||
|
||||
self.kb_proxy = None
|
||||
self.final_result = []
|
||||
self.final_result = {}
|
||||
self.server_vm_create_thread = None
|
||||
self.client_vm_create_thread = None
|
||||
self.kb_runner = None
|
||||
@ -414,6 +415,15 @@ class KloudBuster(object):
|
||||
not self.tenants_list['client'] else self.testing_kloud.flavor_to_use
|
||||
ins.boot_info['user_data'] = str(ins.user_data)
|
||||
|
||||
def gen_metadata(self):
|
||||
self.final_result = {}
|
||||
self.final_result['time'] = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
||||
self.final_result['test_mode'] = 'storage' if self.storage_mode else 'http'
|
||||
if self.storage_mode:
|
||||
self.final_result['storage_target'] = self.client_cfg.storage_target
|
||||
self.final_result['version'] = __version__
|
||||
self.final_result['kb_result'] = []
|
||||
|
||||
def run(self):
|
||||
try:
|
||||
self.stage()
|
||||
@ -528,11 +538,11 @@ class KloudBuster(object):
|
||||
self.print_provision_info()
|
||||
|
||||
def run_test(self, test_only=False):
|
||||
self.final_result = []
|
||||
self.gen_metadata()
|
||||
self.kb_runner.config = self.client_cfg
|
||||
# Run the runner to perform benchmarkings
|
||||
for run_result in self.kb_runner.run(test_only):
|
||||
self.final_result.append(self.kb_runner.tool_result)
|
||||
self.final_result['kb_result'].append(self.kb_runner.tool_result)
|
||||
LOG.info('SUMMARY: %s' % self.final_result)
|
||||
|
||||
def stop_test(self):
|
||||
|
Loading…
Reference in New Issue
Block a user