blob: 08de282251bad12bab24ebeb88d1526e5df18f9e [file] [log] [blame]
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import collections
import logging
import time
from autotest_lib.client.common_lib import error
from autotest_lib.server import site_linux_server
from autotest_lib.server.cros import remote_command
from autotest_lib.server.cros.network import wifi_client
class IperfRunner(object):
"""Helper object to manage iperf test."""
WAIT_TIME_IN_SECONDS = 3
def __init__(self, dut, ap):
"""Initialize.
@param dut: a WiFiClient object, representing DUT.
@param ap: a LinuxCrosRouter object, representing AP.
"""
self._dut = dut
self._ap = ap
def _parse_tcp_results(self, lines):
"""Parses TCP iperf test result.
------------------------------------------------------------
Client connecting to localhost, TCP port 5001
TCP window size: 49.4 KByte (default)
------------------------------------------------------------
[ 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
@returns a Python dictionary containing throughput. Or None.
"""
result = None
tcp_tokens = lines[6].split()
if len(tcp_tokens) >= 6 and tcp_tokens[-1].endswith('bits/sec'):
result = {'throughput': float(tcp_tokens[-2])}
return result
def _parse_udp_results(self, lines):
"""Parses UDP iperf test result.
------------------------------------------------------------
Client connecting to localhost, UDP port 5001
Sending 1470 byte datagrams
UDP buffer size: 108 KByte (default)
------------------------------------------------------------
[ 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 Datagrams
[ 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
@returns a Python dictionary containing throughput, jitter and
number of errors. Or None.
"""
result = None
# Search for the last row containing the word 'Bytes'
mb_row = [row for row,data in enumerate(lines)
if 'Bytes' in data][-1]
udp_tokens = lines[mb_row].replace('/', ' ').split()
# Find the column ending with '...Bytes'
mb_col = [col for col,data in enumerate(udp_tokens)
if data.endswith('Bytes')]
if len(mb_col) > 0 and len(udp_tokens) >= mb_col[0] + 9:
# Make a sublist starting after the column named 'MBytes'
stat_tokens = udp_tokens[mb_col[0]+1:]
result = {'throughput': float(stat_tokens[0]),
'jitter': float(stat_tokens[3]),
'lost': float(stat_tokens[7].strip('()%'))}
return result
def run(self, config):
"""Executes iperf w/ user-specified command-line options
@param config an IperfConfig object, params to run iperf test.
@returns a Python dictionary, iperf test measurements.
"""
results = None
if config.is_downstream:
source_host = self._ap.server
sink_host = self._dut.client
source_cmd = config.get_source_cmd(self._ap.cmd_iperf,
self._dut.wifi_ip)
sink_cmd = config.get_sink_cmd(self._dut.command_iperf)
else:
source_host = self._dut.client
sink_host = self._ap.server
source_cmd = config.get_source_cmd(self._dut.command_iperf,
self._ap.wifi_ip)
sink_cmd = config.get_sink_cmd(self._ap.cmd_iperf)
logging.info('\niperf_source_cmd = %s\niperf_sink_cmd = %s\n',
source_cmd, sink_cmd)
try:
# Open firewall on DUT
self._dut.firewall_open(config.protocol, self._ap.wifi_ip)
# Firewalls are opened on Stumpy at boot time
iperf_thread = remote_command.Command(sink_host, sink_cmd)
# NB: block to allow server time to startup
time.sleep(self.WAIT_TIME_IN_SECONDS)
# Run iperf command and receive command results
t0 = time.time()
timeout = int(config.test_time) + self.WAIT_TIME_IN_SECONDS
results = source_host.run(source_cmd, timeout=timeout)
actual_time = time.time() - t0
logging.info('actual_time: %f', actual_time)
iperf_thread.join()
finally:
# Close up DUT firewall
self._dut.firewall_cleanup()
logging.info(results)
lines = results.stdout.splitlines()
# Each test type has a different form of output
if config.protocol == config.PROTOCOL_TCP:
return self._parse_tcp_results(lines)
elif config.protocol == config.PROTOCOL_UDP:
return self._parse_udp_results(lines)