import os, time, logging from autotest_lib.client.bin import test, utils from autotest_lib.client.bin.net import net_utils from autotest_lib.client.common_lib import error MPSTAT_IX = 0 NETPERF_IX = 1 class netperf2(test.test): version = 4 # ftp://ftp.netperf.org/netperf/netperf-2.4.5.tar.bz2 def setup(self, tarball = 'netperf-2.4.5.tar.bz2'): self.job.require_gcc() tarball = utils.unmap_url(self.bindir, tarball, self.tmpdir) utils.extract_tarball_to_dir(tarball, self.srcdir) os.chdir(self.srcdir) utils.system('patch -p0 < ../wait_before_data.patch') utils.configure() utils.make() def initialize(self): self.server_prog = '%s&' % os.path.join(self.srcdir, 'src/netserver') self.client_prog = '%s' % os.path.join(self.srcdir, 'src/netperf') self.valid_tests = ['TCP_STREAM', 'TCP_MAERTS', 'TCP_RR', 'TCP_CRR', 'TCP_SENDFILE', 'UDP_STREAM', 'UDP_RR'] self.results = [] self.actual_times = [] self.netif = '' self.network = net_utils.network() self.network_utils = net_utils.network_utils() def run_once(self, server_ip, client_ip, role, test = 'TCP_STREAM', test_time = 15, stream_list = [1], test_specific_args = '', cpu_affinity = '', dev = '', bidi = False, wait_time = 5): """ server_ip: IP address of host running netserver client_ip: IP address of host running netperf client(s) role: 'client' or 'server' test: one of TCP_STREAM, TCP_MEARTS, TCP_RR, TCP_CRR, TCP_SENDFILE, UDP_STREAM or UDP_RR test_time: time to run the test for in seconds stream_list: list of number of netperf streams to launch test_specific_args: Optional test specific args. For example to set the request,response size for RR tests to 200,100, set it to: '-- -r 200,100'. Or, to set the send buffer size of STREAM tests to 200, set it to: '-- -m 200' cpu_affinity: netperf/netserver processes will get taskset to the cpu_affinity. cpu_affinity is specified as a bitmask in hex without the leading 0x. For example, to run on CPUs 0 & 5, cpu_affinity needs to be '21' dev: device on which to run traffic on. For example, to run on inteface eth1, set it to 'eth1'. bidi: bi-directional traffic. This is supported for TCP_STREAM test only. The RR & CRR tests are bi-directional by nature. wait_time: Time to wait after establishing data/control connections but before sending data traffic. """ if test not in self.valid_tests: raise error.TestError('invalid test specified') self.role = role self.test = test self.test_time = test_time self.wait_time = wait_time self.stream_list = stream_list self.bidi = bidi server_tag = server_ip + '#netperf-server' client_tag = client_ip + '#netperf-client' all = [server_tag, client_tag] # If a specific device has been requested, configure it. if dev: timeout = 60 if role == 'server': self.configure_interface(dev, server_ip) self.ping(client_ip, timeout) else: self.configure_interface(dev, client_ip) self.ping(server_ip, timeout) for num_streams in stream_list: if role == 'server': self.server_start(cpu_affinity) try: # Wait up to ten minutes for the client to reach this # point. self.job.barrier(server_tag, 'start_%d' % num_streams, 600).rendezvous(*all) # Wait up to test_time + 5 minutes for the test to # complete self.job.barrier(server_tag, 'stop_%d' % num_streams, test_time+300).rendezvous(*all) finally: self.server_stop() elif role == 'client': # Wait up to ten minutes for the server to start self.job.barrier(client_tag, 'start_%d' % num_streams, 600).rendezvous(*all) self.client(server_ip, test, test_time, num_streams, test_specific_args, cpu_affinity) # Wait up to 5 minutes for the server to also reach this point self.job.barrier(client_tag, 'stop_%d' % num_streams, 300).rendezvous(*all) else: raise error.TestError('invalid role specified') self.restore_interface() def configure_interface(self, dev, ip_addr): self.netif = net_utils.netif(dev) self.netif.up() if self.netif.get_ipaddr() != ip_addr: self.netif.set_ipaddr(ip_addr) def restore_interface(self): if self.netif: self.netif.restore() def server_start(self, cpu_affinity): utils.system('killall netserver', ignore_status=True) cmd = self.server_prog if cpu_affinity: cmd = 'taskset %s %s' % (cpu_affinity, cmd) self.results.append(utils.system_output(cmd, retain_output=True)) def server_stop(self): utils.system('killall netserver', ignore_status=True) def client(self, server_ip, test, test_time, num_streams, test_specific_args, cpu_affinity): args = '-H %s -t %s -l %d' % (server_ip, test, test_time) if self.wait_time: args += ' -s %d ' % self.wait_time # Append the test specific arguments. if test_specific_args: args += ' ' + test_specific_args cmd = '%s %s' % (self.client_prog, args) if cpu_affinity: cmd = 'taskset %s %s' % (cpu_affinity, cmd) try: cmds = [] # Get 5 mpstat samples. Since tests with large number of streams # take a long time to start up all the streams, we'll toss out the # first and last sample when recording results interval = max(1, test_time / 5) cmds.append('sleep %d && %s -P ALL %s 5' % (self.wait_time, 'mpstat', interval)) # Add the netperf commands for i in xrange(num_streams): cmds.append(cmd) if self.bidi and test == 'TCP_STREAM': cmds.append(cmd.replace('TCP_STREAM', 'TCP_MAERTS')) t0 = time.time() # Launch all commands in parallel out = utils.run_parallel(cmds, timeout=test_time + 500, ignore_status=True) t1 = time.time() self.results.append(out) self.actual_times.append(t1 - t0 - self.wait_time) # Log test output logging.info(out) except error.CmdError, e: """ Catch errors due to timeout, but raise others The actual error string is: "Command did not complete within %d seconds" called in function join_bg_job in the file common_lib/utils.py Looking for 'within' is probably not the best way to do this but works for now""" if ('within' in e.additional_text or 'non-zero' in e.additional_text): logging.debug(e.additional_text) self.results.append(None) self.actual_times.append(1) else: raise def postprocess(self): if self.role == 'client': # if profilers are enabled, the test gets runs twice if (len(self.stream_list) != len(self.results) and 2*len(self.stream_list) != len(self.results)): raise error.TestError('Mismatched number of results') function = None keys = None # Each of the functions return tuples in which the keys define # what that item in the tuple represents if self.test in ['TCP_STREAM', 'TCP_MAERTS', 'TCP_SENDFILE']: function = self.process_tcp_stream keys = ('Throughput',) elif self.test == 'UDP_STREAM': function = self.process_udp_stream keys = ('Throughput', 'Errors') elif self.test in ['TCP_RR', 'TCP_CRR', 'UDP_RR']: function = self.process_request_response keys = ('Transfer_Rate',) else: raise error.TestError('Unhandled test') for i, streams in enumerate(self.stream_list): attr = {'stream_count':streams} keyval = {} temp_vals = [] # Short circuit to handle errors due to client timeouts if not self.results[i]: self.write_iteration_keyval(attr, keyval) continue # Collect output of netperf sessions failed_streams_count = 0 for result in self.results[i][NETPERF_IX:]: if result.exit_status: failed_streams_count += 1 else: temp_vals.append(function(result.stdout)) keyval['Failed_streams_count'] = failed_streams_count # Process mpstat output mpstat_out = self.results[i][MPSTAT_IX].stdout cpu_stats = self.network_utils.process_mpstat(mpstat_out, 5) keyval['CPU_C'] = 100 - cpu_stats['idle'] keyval['CPU_C_SYS'] = cpu_stats['sys'] keyval['CPU_C_HI'] = cpu_stats['irq'] keyval['CPU_C_SI'] = cpu_stats['soft'] keyval['INTRS_C'] = cpu_stats['intr/s'] actual_time = self.actual_times[i] keyval['actual_time'] = actual_time logging.info('actual_time: %f', actual_time) # Compute the sum of elements returned from function which # represent the string contained in keys for j, key in enumerate(keys): vals = [x[j] for x in temp_vals] # scale result by the actual time taken keyval[key] = sum(vals) # record 'Efficiency' as perf/CPU if keyval['CPU_C'] != 0: keyval['Efficieny_C'] = keyval[keys[0]]/keyval['CPU_C'] else: keyval['Efficieny_C'] = keyval[keys[0]] self.write_iteration_keyval(attr, keyval) def process_tcp_stream(self, output): """Parses the following (works for both TCP_STREAM, TCP_MAERTS and TCP_SENDFILE) and returns a singleton containing throughput. TCP STREAM TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET to foo.bar.com \ (10.10.10.3) port 0 AF_INET Recv Send Send Socket Socket Message Elapsed Size Size Size Time Throughput bytes bytes bytes secs. 10^6bits/sec 87380 16384 16384 2.00 941.28 """ return float(output.splitlines()[6].split()[4]), def process_udp_stream(self, output): """Parses the following and returns a touple containing throughput and the number of errors. UDP UNIDIRECTIONAL SEND TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET \ to foo.bar.com (10.10.10.3) port 0 AF_INET Socket Message Elapsed Messages Size Size Time Okay Errors Throughput bytes bytes secs # # 10^6bits/sec 129024 65507 2.00 3673 0 961.87 131072 2.00 3673 961.87 """ line_tokens = output.splitlines()[5].split() return float(line_tokens[5]), int(line_tokens[4]) def process_request_response(self, output): """Parses the following which works for both rr (TCP and UDP) and crr tests and returns a singleton containing transfer rate. TCP REQUEST/RESPONSE TEST from 0.0.0.0 (0.0.0.0) port 0 AF_INET \ to foo.bar.com (10.10.10.3) port 0 AF_INET Local /Remote Socket Size Request Resp. Elapsed Trans. Send Recv Size Size Time Rate bytes Bytes bytes bytes secs. per sec 16384 87380 1 1 2.00 14118.53 16384 87380 """ return float(output.splitlines()[6].split()[5]), def ping(self, ip, timeout): curr_time = time.time() end_time = curr_time + timeout while curr_time < end_time: if not os.system('ping -c 1 ' + ip): # Ping succeeded return # Ping failed. Lets sleep a bit and try again. time.sleep(5) curr_time = time.time() return