Added iperf executor with chart feature
Change-Id: I5dd6c56c615c86a4fc3400a951d9739205cff604
This commit is contained in:
parent
de78bbdcff
commit
f5c694b9cd
@ -17,8 +17,8 @@ To install:
|
||||
3. ``shaker-image-builder`` - builds image for agent VMs inside OpenStack
|
||||
|
||||
Note: image builder is able to create Nova flavor optimized for the image and this requires
|
||||
admin user privileges. However if the flavor is already exists then it can be provided via
|
||||
``flavor-name`` config parameter and the tool executed from an ordinary user.
|
||||
admin user privileges. However if the flavor is already exists then it can be provided via
|
||||
``flavor-name`` config parameter and the tool executed from an ordinary user.
|
||||
|
||||
How to run
|
||||
----------
|
||||
|
@ -1,27 +1,25 @@
|
||||
description:
|
||||
This scenario launches pairs of VMs in the same private network
|
||||
This scenario launches pairs of VMs in the same private network. Every VM is
|
||||
hosted on a separate compute node.
|
||||
|
||||
deployment:
|
||||
template: scenarios/networking/same_net.hot
|
||||
template_parameters:
|
||||
image: shaker-image
|
||||
flavor: shaker-flavor
|
||||
private_net_cidr: 10.0.0.0/24
|
||||
vm_accommodation: [pair, single_room]
|
||||
|
||||
execution:
|
||||
size: 'quadratic_progression'
|
||||
tests:
|
||||
-
|
||||
title: iperf TCP test
|
||||
class: iperf
|
||||
title: Iperf TCP test
|
||||
class: iperf_graph
|
||||
time: 60
|
||||
-
|
||||
title: Iperf UDP 5 threads
|
||||
class: iperf
|
||||
udp: 1
|
||||
mss: 1406
|
||||
threads: 5
|
||||
-
|
||||
title: Netperf TCP_STREAM
|
||||
class: netperf
|
||||
method: TCP_STREAM
|
||||
time: 60
|
||||
|
@ -13,6 +13,9 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
import collections
|
||||
import csv
|
||||
|
||||
from shaker.openstack.common import log as logging
|
||||
|
||||
|
||||
@ -63,21 +66,66 @@ class IperfExecutor(BaseExecutor):
|
||||
def get_command(self):
|
||||
target = self.agent['slave']['ip']
|
||||
mss = self.test_definition.get('mss')
|
||||
interval = self.test_definition.get('interval')
|
||||
return ('sudo nice -n -20 iperf --client %(target)s --format m'
|
||||
'%(mss)s --len %(bs)s --print_mss --nodelay'
|
||||
'%(udp)s --time %(time)s --parallel %(threads)s' %
|
||||
'%(mss)s --len %(bs)s --nodelay'
|
||||
'%(udp)s --time %(time)s --parallel %(threads)s'
|
||||
'%(css)s %(interval)s' %
|
||||
dict(target=self.test_definition.get('target') or target,
|
||||
mss=mss and '--mss %s' % mss or '',
|
||||
mss=mss and ' --mss %s' % mss or '',
|
||||
bs=self.test_definition.get('buffer_size') or '8k',
|
||||
udp=self.test_definition.get('udp') and '--udp' or '',
|
||||
udp=self.test_definition.get('udp') and ' --udp' or '',
|
||||
threads=self.test_definition.get('threads') or 1,
|
||||
time=self.test_definition.get('time') or 60))
|
||||
time=self.test_definition.get('time') or 60,
|
||||
css=self.test_definition.get('css') and ' -y C' or '',
|
||||
interval=interval and '--interval %s' % interval or ''))
|
||||
|
||||
|
||||
class IperfGraphExecutor(IperfExecutor):
|
||||
def get_command(self):
|
||||
self.test_definition['css'] = True
|
||||
self.test_definition['interval'] = '1'
|
||||
return super(IperfGraphExecutor, self).get_command()
|
||||
|
||||
def process_reply(self, message):
|
||||
result = super(IperfGraphExecutor, self).process_reply(message)
|
||||
|
||||
samples = collections.defaultdict(list)
|
||||
|
||||
for row in csv.reader(result['stdout'].split('\n')):
|
||||
if row:
|
||||
thread = row[5]
|
||||
samples[thread].append(dict(
|
||||
time=float(row[6].split('-')[1]),
|
||||
transfer=int(row[7]),
|
||||
bandwidth=int(row[8]),
|
||||
))
|
||||
|
||||
result['samples'] = samples
|
||||
|
||||
# calc max, min, avg per thread
|
||||
bandwidth_max = collections.defaultdict(float)
|
||||
bandwidth_min = collections.defaultdict(float)
|
||||
bandwidth_avg = collections.defaultdict(float)
|
||||
|
||||
for thread, data in samples.items():
|
||||
arr = [s['bandwidth'] for s in samples[thread]]
|
||||
bandwidth_max[thread] = max(arr)
|
||||
bandwidth_min[thread] = min(arr)
|
||||
bandwidth_avg[thread] = sum(arr) / len(arr)
|
||||
|
||||
result['bandwidth_max'] = bandwidth_max
|
||||
result['bandwidth_min'] = bandwidth_min
|
||||
result['bandwidth_avg'] = bandwidth_avg
|
||||
|
||||
return result
|
||||
|
||||
|
||||
EXECUTORS = {
|
||||
'shell': ShellExecutor,
|
||||
'netperf': NetperfExecutor,
|
||||
'iperf': IperfExecutor,
|
||||
'iperf_graph': IperfGraphExecutor,
|
||||
'netperf_wrapper': NetperfWrapperExecutor,
|
||||
'_default': ShellExecutor,
|
||||
}
|
||||
|
@ -14,31 +14,16 @@
|
||||
# limitations under the License.
|
||||
|
||||
import sys
|
||||
import uuid
|
||||
|
||||
import jinja2
|
||||
|
||||
from shaker.engine import utils
|
||||
|
||||
|
||||
def _add_uuids(d):
|
||||
for k, v in d.items():
|
||||
if isinstance(v, dict):
|
||||
_add_uuids(v)
|
||||
elif isinstance(v, list):
|
||||
for i in v:
|
||||
if isinstance(i, dict):
|
||||
_add_uuids(i)
|
||||
else:
|
||||
d['uuid'] = uuid.uuid4()
|
||||
|
||||
|
||||
def generate_report(report_template, report_filename, data):
|
||||
template = utils.read_file(report_template)
|
||||
compiled_template = jinja2.Template(template)
|
||||
|
||||
_add_uuids(data)
|
||||
|
||||
rendered_template = compiled_template.render(dict(report=data))
|
||||
|
||||
if report_filename:
|
||||
|
@ -15,6 +15,11 @@
|
||||
<link rel="stylesheet"
|
||||
href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">
|
||||
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
|
||||
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/c3/0.4.9/c3.min.js"></script>
|
||||
|
||||
<style type="text/css">
|
||||
body {
|
||||
@ -79,6 +84,224 @@
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/*-- Chart --*/
|
||||
|
||||
|
||||
.c3 svg {
|
||||
font: 10px sans-serif;
|
||||
}
|
||||
.c3 path, .c3 line {
|
||||
fill: none;
|
||||
stroke: #000;
|
||||
}
|
||||
.c3 text {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.c3-legend-item-tile,
|
||||
.c3-xgrid-focus,
|
||||
.c3-ygrid,
|
||||
.c3-event-rect,
|
||||
.c3-bars path {
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.c3-chart-arc path {
|
||||
stroke: #fff;
|
||||
|
||||
}
|
||||
.c3-chart-arc text {
|
||||
fill: #fff;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/*-- Axis --*/
|
||||
|
||||
.c3-axis-x .tick {
|
||||
}
|
||||
.c3-axis-x-label {
|
||||
}
|
||||
|
||||
.c3-axis-y .tick {
|
||||
}
|
||||
.c3-axis-y-label {
|
||||
}
|
||||
|
||||
.c3-axis-y2 .tick {
|
||||
}
|
||||
.c3-axis-y2-label {
|
||||
}
|
||||
|
||||
/*-- Grid --*/
|
||||
|
||||
.c3-grid line {
|
||||
stroke: #aaa;
|
||||
}
|
||||
.c3-grid text {
|
||||
fill: #aaa;
|
||||
}
|
||||
.c3-xgrid, .c3-ygrid {
|
||||
stroke-dasharray: 3 3;
|
||||
}
|
||||
.c3-xgrid-focus {
|
||||
}
|
||||
|
||||
/*-- Text on Chart --*/
|
||||
|
||||
.c3-text {
|
||||
}
|
||||
|
||||
.c3-text.c3-empty {
|
||||
fill: #808080;
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
/*-- Line --*/
|
||||
|
||||
.c3-line {
|
||||
stroke-width: 1px;
|
||||
}
|
||||
/*-- Point --*/
|
||||
|
||||
.c3-circle._expanded_ {
|
||||
stroke-width: 1px;
|
||||
stroke: white;
|
||||
}
|
||||
.c3-selected-circle {
|
||||
fill: white;
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
/*-- Bar --*/
|
||||
|
||||
.c3-bar {
|
||||
stroke-width: 0;
|
||||
}
|
||||
.c3-bar._expanded_ {
|
||||
fill-opacity: 0.75;
|
||||
}
|
||||
|
||||
/*-- Arc --*/
|
||||
|
||||
.c3-chart-arcs-title {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
/*-- Focus --*/
|
||||
|
||||
.c3-target.c3-focused {
|
||||
opacity: 1;
|
||||
}
|
||||
.c3-target.c3-focused path.c3-line, .c3-target.c3-focused path.c3-step {
|
||||
stroke-width: 2px;
|
||||
}
|
||||
.c3-target.c3-defocused {
|
||||
opacity: 0.3 !important;
|
||||
}
|
||||
|
||||
|
||||
/*-- Region --*/
|
||||
|
||||
.c3-region {
|
||||
fill: steelblue;
|
||||
fill-opacity: .1;
|
||||
}
|
||||
|
||||
/*-- Brush --*/
|
||||
|
||||
.c3-brush .extent {
|
||||
fill-opacity: .1;
|
||||
}
|
||||
|
||||
/*-- Select - Drag --*/
|
||||
|
||||
.c3-dragarea {
|
||||
}
|
||||
|
||||
/*-- Legend --*/
|
||||
|
||||
.c3-legend-item {
|
||||
font-size: 12px;
|
||||
}
|
||||
.c3-legend-item-hidden {
|
||||
opacity: 0.15;
|
||||
}
|
||||
|
||||
.c3-legend-background {
|
||||
opacity: 0.75;
|
||||
fill: white;
|
||||
stroke: lightgray;
|
||||
stroke-width: 1
|
||||
}
|
||||
|
||||
/*-- Tooltip --*/
|
||||
|
||||
.c3-tooltip-container {
|
||||
z-index: 10;
|
||||
}
|
||||
.c3-tooltip {
|
||||
border-collapse:collapse;
|
||||
border-spacing:0;
|
||||
background-color:#fff;
|
||||
empty-cells:show;
|
||||
-webkit-box-shadow: 7px 7px 12px -9px rgb(119,119,119);
|
||||
-moz-box-shadow: 7px 7px 12px -9px rgb(119,119,119);
|
||||
box-shadow: 7px 7px 12px -9px rgb(119,119,119);
|
||||
opacity: 0.9;
|
||||
}
|
||||
.c3-tooltip tr {
|
||||
border:1px solid #CCC;
|
||||
}
|
||||
.c3-tooltip th {
|
||||
background-color: #aaa;
|
||||
font-size:14px;
|
||||
padding:2px 5px;
|
||||
text-align:left;
|
||||
color:#FFF;
|
||||
}
|
||||
.c3-tooltip td {
|
||||
font-size:13px;
|
||||
padding: 3px 6px;
|
||||
background-color:#fff;
|
||||
border-left:1px dotted #999;
|
||||
}
|
||||
.c3-tooltip td > span {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-right: 6px;
|
||||
}
|
||||
.c3-tooltip td.value{
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.c3-area {
|
||||
stroke-width: 0;
|
||||
opacity: 0.2;
|
||||
}
|
||||
|
||||
.c3-chart-arcs .c3-chart-arcs-background {
|
||||
fill: #e0e0e0;
|
||||
stroke: none;
|
||||
}
|
||||
.c3-chart-arcs .c3-chart-arcs-gauge-unit {
|
||||
fill: #000;
|
||||
font-size: 16px;
|
||||
}
|
||||
.c3-chart-arcs .c3-chart-arcs-gauge-max {
|
||||
fill: #777;
|
||||
}
|
||||
.c3-chart-arcs .c3-chart-arcs-gauge-min {
|
||||
fill: #777;
|
||||
}
|
||||
|
||||
.c3-chart-arc .c3-gauge-value {
|
||||
fill: #000;
|
||||
/* font-size: 28px !important;*/
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
@ -116,7 +339,7 @@
|
||||
{% for test in tests %}
|
||||
<li><a href="#test-{{ test.definition.uuid }}" data-toggle="tab">
|
||||
{% if test.definition.title %}
|
||||
{{ test.definition.title|title }}
|
||||
{{ test.definition.title }}
|
||||
{% else %}
|
||||
Test {{ test.definition }}
|
||||
{% endif %}
|
||||
@ -165,6 +388,33 @@
|
||||
<h4>Agent {{ result_per_agent.agent.id }}
|
||||
({{ result_per_agent.agent.ip }})</h4>
|
||||
|
||||
{% if result_per_agent.samples %}
|
||||
{# <div>{{ result_per_agent.samples }}</div>#}
|
||||
<div id="chart-{{ result_per_agent.uuid }}"></div>
|
||||
<script type="application/javascript">
|
||||
var chart = c3.generate({
|
||||
bindto: '#chart-{{ result_per_agent.uuid }}',
|
||||
data: {
|
||||
x: 'x',
|
||||
columns: [
|
||||
{# {% set first_row = first(result_per_agent.samples) %}#}
|
||||
{# ['x', {{ first_row|map(attribute='time')|join(', ') }}],#}
|
||||
|
||||
{% for thread, array in result_per_agent.samples.items() %}
|
||||
['x', {{ array|map(attribute='time')|join(', ') }}],
|
||||
['thread{{ thread }}', {{ array|map(attribute='bandwidth')|join(', ') }}]
|
||||
{% endfor %}
|
||||
],
|
||||
types: { thread3: 'area' }
|
||||
},
|
||||
axis: {
|
||||
x: { label: 'time' },
|
||||
y: { label: 'Bandwidth, bps' }
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
|
||||
{% if result_per_agent.command %}
|
||||
<h5>Command:</h5>
|
||||
<pre>{{ result_per_agent.command }}</pre>
|
||||
@ -188,11 +438,5 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bootstrap core JavaScript
|
||||
================================================== -->
|
||||
<!-- Placed at the end of the document so the pages load faster -->
|
||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script>
|
||||
<!-- Latest compiled and minified JavaScript -->
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -14,6 +14,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from oslo_config import cfg
|
||||
import yaml
|
||||
@ -175,11 +176,15 @@ def execute(execution, agents):
|
||||
for a in selected_agents)
|
||||
|
||||
test_case_result = quorum.run_test_case(executors)
|
||||
values = test_case_result.values()
|
||||
for v in values:
|
||||
v['uuid'] = uuid.uuid4()
|
||||
results_per_iteration.append({
|
||||
'agents': selected_agents,
|
||||
'results_per_agent': test_case_result.values(),
|
||||
'results_per_agent': values,
|
||||
})
|
||||
|
||||
test['uuid'] = uuid.uuid4()
|
||||
result.append({
|
||||
'results_per_iteration': results_per_iteration,
|
||||
'definition': test,
|
||||
|
60
tests/test_iperf_graph_executor.py
Normal file
60
tests/test_iperf_graph_executor.py
Normal file
@ -0,0 +1,60 @@
|
||||
# Copyright (c) 2015 Mirantis Inc.
|
||||
#
|
||||
# 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.
|
||||
|
||||
import testtools
|
||||
|
||||
from shaker.engine import executors
|
||||
|
||||
|
||||
IP = '10.0.0.10'
|
||||
AGENT = {'slave': {'ip': IP}}
|
||||
|
||||
|
||||
class TestIperfGraphExecutor(testtools.TestCase):
|
||||
|
||||
def test_get_command(self):
|
||||
executor = executors.IperfGraphExecutor({}, AGENT)
|
||||
|
||||
expected = ('sudo nice -n -20 iperf --client %s --format m '
|
||||
'--len 8k --nodelay --time 60 --parallel 1 '
|
||||
'-y C --interval 1') % IP
|
||||
self.assertEqual(expected, executor.get_command())
|
||||
|
||||
def test_process_reply(self):
|
||||
executor = executors.IperfGraphExecutor({}, AGENT)
|
||||
message = {
|
||||
'stdout': """
|
||||
20150224134955,172.1.7.77,47351,172.1.76.77,5001,3,0.0-1.0,500686848,4005494784
|
||||
20150224134956,172.1.7.77,47351,172.1.76.77,5001,3,1.0-2.0,516055040,4128440320
|
||||
20150224134957,172.1.7.77,47351,172.1.76.77,5001,3,2.0-3.0,508436480,4067491840
|
||||
"""
|
||||
}
|
||||
expected = {
|
||||
'samples': {
|
||||
'3': [
|
||||
dict(time=1.0, transfer=500686848, bandwidth=4005494784),
|
||||
dict(time=2.0, transfer=516055040, bandwidth=4128440320),
|
||||
dict(time=3.0, transfer=508436480, bandwidth=4067491840),
|
||||
]
|
||||
},
|
||||
'bandwidth_max': {'3': 4128440320},
|
||||
'bandwidth_min': {'3': 4005494784},
|
||||
'bandwidth_avg': {'3': (4005494784 + 4128440320 + 4067491840) / 3},
|
||||
}
|
||||
reply = executor.process_reply(message)
|
||||
self.assertEqual(expected['samples'], reply['samples'])
|
||||
self.assertEqual(expected['bandwidth_max'], reply['bandwidth_max'])
|
||||
self.assertEqual(expected['bandwidth_min'], reply['bandwidth_min'])
|
||||
self.assertEqual(expected['bandwidth_avg'], reply['bandwidth_avg'])
|
Loading…
x
Reference in New Issue
Block a user