vmtp/vmtp/iperf_tool.py
Cédric Ollivier dc79be8a3b Update all modules to py3
It's mostly done via 2to3 and completed by manual updates fixing
TypeErrors and former false print calls.

It has been double checked via Functest which cannot cover all logics.

Change-Id: If272524f147735a942a84ce1d2bec4e3423817c2
Signed-off-by: Cédric Ollivier <ollivier.cedric@gmail.com>
2019-05-22 21:13:05 +02:00

226 lines
9.9 KiB
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.
#
import re
from .log import LOG
from .perf_tool import PerfTool
# The resulting unit should be in K
MULTIPLIERS = {'K': 1,
'M': 1.0e3,
'G': 1.0e6}
def get_bdw_kbps(bdw, bdw_unit):
if not bdw_unit:
# bits/sec
return bdw / 1000
if bdw_unit in MULTIPLIERS:
return int(bdw * MULTIPLIERS[bdw_unit])
LOG.error('Error: unknown multiplier: ' + bdw_unit)
return bdw
class IperfTool(PerfTool):
def __init__(self, instance):
PerfTool.__init__(self, 'iperf-2.0.12', instance)
def get_server_launch_cmd(self):
'''Return the command to launch the server side.'''
# Need 1 server for tcp (port 5001) and 1 for udp (port 5001)
if self.instance.config.ipv6_mode:
return [self.dest_path + ' -s -V >/dev/null &',
self.dest_path + ' -s -u -V >/dev/null &']
else:
return [self.dest_path + ' -s >/dev/null &',
self.dest_path + ' -s -u >/dev/null &']
def run_client(self, target_ip, target_instance,
mss=None, bandwidth=0, bidirectional=False):
'''Run the test
:return: list containing one or more dictionary results
'''
res_list = []
# Get list of protocols and packet sizes to measure
(proto_list, proto_pkt_sizes) = self.get_proto_profile()
for proto, pkt_size_list in zip(proto_list, proto_pkt_sizes):
# bidirectional is not supported for udp
# (need to find the right iperf options to make it work as there are
# issues for the server to send back results to the client in reverse
# direction
if proto == 'TCP':
# For accuracy purpose, TCP throughput will be measured multiple times
loop_count = self.instance.config.tcp_tp_loop_count
self.instance.display('Measuring TCP Throughput...')
for _ in range(loop_count):
# for bidirectional the function returns a list of 2 results
res = self.run_client_dir(target_ip, mss,
bandwidth_kbps=bandwidth,
bidirectional=bidirectional,
protocol=proto,
length=pkt_size_list[0])
res_list.extend(res)
else:
for pkt_size in pkt_size_list:
self.instance.display('Measuring UDP Throughput (packet size=%d)...', pkt_size)
# Trying a second time if needed, due to iperf bugs...
for _ in range(3):
res = self.run_client_dir(target_ip, mss,
bandwidth_kbps=bandwidth,
bidirectional=False,
protocol=proto,
length=pkt_size)
if 'error' not in res:
break
res_list.extend(res)
return res_list
def run_client_dir(self, target_ip,
mss,
bidirectional=False,
bandwidth_kbps=0,
protocol='TCP',
length=0,
no_cpu_timed=0):
'''Run client for given protocol and packet size
:param bandwidth_kbps: transmit rate limit in Kbps
:param udp: if true get UDP throughput, else get TCP throughput
:param length: length of network write|read buf (default 1K|8K/udp, 64K/tcp)
for udp is the packet size
:param no_cpu_timed: if non zero will disable cpu collection and override
the time with the provided value - used mainly for udp
to find quickly the optimal throughput using short
tests at various throughput values
:return: a list of dictionary with the 1 or 2 results (see parse_results())
'''
# run client using the default TCP window size (tcp window
# scaling is normally enabled by default so setting explicit window
# size is not going to help achieve better results)
opts = ''
udp = protocol == "UDP"
# run iperf client using the default TCP window size (tcp window
# scaling is normally enabled by default so setting explicit window
# size is not going to help achieve better results)
if mss:
opts += " -M " + str(mss)
if bidirectional:
opts += " -r"
if length:
opts += " -l" + str(length)
if self.instance.config.ipv6_mode:
opts += " -V "
if udp:
opts += " -u"
# for UDP if the bandwidth is not provided we need to calculate
# the optimal bandwidth
if not bandwidth_kbps:
udp_res = self.find_bdw(length, target_ip, protocol)
if 'error' in udp_res:
return [udp_res]
if not self.instance.gmond_svr:
# if we do not collect CPU we might as well return
# the results found through iteration
return [udp_res]
bandwidth_kbps = udp_res['throughput_kbps']
if bandwidth_kbps:
opts += " -b%dK" % (bandwidth_kbps)
if no_cpu_timed:
duration_sec = no_cpu_timed
else:
duration_sec = self.instance.get_cmd_duration()
cmd = "%s -c %s -t %d %s" % (self.dest_path,
target_ip,
duration_sec,
opts)
self.instance.buginf(cmd)
if no_cpu_timed:
# force the timeout value with 20 second extra for the command to
# complete and do not collect CPU
cpu_load = None
cmd_out = self.instance.exec_command(cmd, duration_sec + 30)
else:
(cmd_out, cpu_load) = self.instance.exec_with_cpu(cmd)
if udp:
# Decode UDP output (unicast and multicast):
#
# [ 3] local 127.0.0.1 port 54244 connected with 127.0.0.1 port 5001
# [ ID] Interval Transfer Bandwidth
# [ 3] 0.0-10.0 sec 1.25 MBytes 1.05 Mbits/sec
# [ 3] Sent 893 datagrams
# [ 3] Server Report:
# [ ID] Interval Transfer Bandwidth Jitter Lost/Total Da
# [ 3] 0.0-10.0 sec 1.25 MBytes 1.05 Mbits/sec 0.032 ms 1/894 (0.11%)
# [ 3] 0.0-15.0 sec 14060 datagrams received out-of-order
re_udp = r'([\d\.]*)\s*([KMG]?)bits/sec\s*[\d\.]*\s*ms\s*(\d*)/\s*(\d*) '
match = re.search(re_udp, cmd_out)
if match:
bdw = float(match.group(1))
bdw_unit = match.group(2)
drop = float(match.group(3))
pkt = int(match.group(4))
# iperf uses multiple of 1000 for K - not 1024
return [self.parse_results('UDP',
get_bdw_kbps(bdw, bdw_unit),
lossrate=round(drop * 100 / pkt, 2),
msg_size=length,
cpu_load=cpu_load)]
else:
# TCP output:
# [ 3] local 127.0.0.1 port 57936 connected with 127.0.0.1 port 5001
# [ ID] Interval Transfer Bandwidth
# [ 3] 0.0-10.0 sec 2.09 GBytes 1.79 Gbits/sec
#
# For bi-directional option (-r), last 3 lines:
# [ 5] 0.0-10.0 sec 36.0 GBytes 31.0 Gbits/sec
# [ 4] local 127.0.0.1 port 5002 connected with 127.0.0.1 port 39118
# [ 4] 0.0-10.0 sec 36.0 GBytes 30.9 Gbits/sec
re_tcp = r'Bytes\s*([\d\.]*)\s*([KMG])bits/sec'
match = re.search(re_tcp, cmd_out)
if match:
bdw = float(match.group(1))
bdw_unit = match.group(2)
res = [self.parse_results('TCP',
get_bdw_kbps(bdw, bdw_unit),
msg_size=length,
cpu_load=cpu_load)]
if bidirectional:
# decode the last row results
re_tcp = r'Bytes\s*([\d\.]*)\s*([KMG])bits/sec$'
match = re.search(re_tcp, cmd_out)
if match:
bdw = float(match.group(1))
bdw_unit = match.group(2)
# use the same cpu load since the same run
# does both directions
res.append(self.parse_results('TCP',
get_bdw_kbps(bdw, bdw_unit),
reverse_dir=True,
msg_size=length,
cpu_load=cpu_load))
return res
return [self.parse_error(protocol, 'Could not parse: %s' % (cmd_out))]