1########################################################################## 2# Copyright (c) 2009, ETH Zurich. 3# All rights reserved. 4# 5# This file is distributed under the terms in the attached LICENSE file. 6# If you do not find this file, copies can be found by writing to: 7# ETH Zurich D-INFK, Haldeneggsteig 4, CH-8092 Zurich. Attn: Systems Group. 8########################################################################## 9 10import re, os, subprocess, select, time, datetime 11import tests, debug, siteconfig 12from common import TestCommon, TimeoutError, select_timeout 13from results import RowResults 14 15# TODO: this test needs a control loop like the httperf test to 16# ramp up the load until it saturates. currently it just runs N 17# iterations of a pre-configured set of parameters. 18 19IPBENCH_TEST = 'latency' 20IPBENCH_TEST_ARGS = ['Mbps=400', 'size=1300'] # per daemon 21IPBENCH_NDAEMONS = 3 22IPBENCH_ITERATIONS = 4 23IPBENCH_TIMEOUT = datetime.timedelta(seconds=60) # for a single ipbench run 24IPBENCH_SLEEPTIME = 10 # seconds between iterations 25 26LOGFILENAME = 'testlog.txt' 27 28class EchoTestCommon(TestCommon): 29 def setup(self, build, machine, testdir): 30 super(EchoTestCommon, self).setup(build, machine, testdir) 31 self.testdir = testdir 32 self.finished = False 33 34 def get_modules(self, build, machine): 35 cardName = "e1000" 36 modules = super(EchoTestCommon, self).get_modules(build, machine) 37 modules.add_module("e1000n", ["auto"]) 38 modules.add_module("NGD_mng", ["auto"]) 39 modules.add_module("netd", ["auto"]) 40 modules.add_module("echoserver",["core=%d"%machine.get_coreids()[3], 41 "cardname=%s"%cardName]) 42 return modules 43 44 def process_line(self, line): 45 m = re.match(r'Interface up! IP address (\d+\.\d+\.\d+\.\d+)', line) 46 if m: 47 self.run_test(m.group(1)) 48 self.finished = True 49 50 def is_finished(self, line): 51 return self.finished or super(EchoTestCommon, self).is_finished(line) 52 53 def get_ipbench_test(self): 54 return (IPBENCH_TEST, IPBENCH_TEST_ARGS) 55 56 def _run_ipbenchd(self, user, host): 57 ssh_dest = '%s@%s' % (user, host) 58 remotecmd = siteconfig.get('IPBENCHD_PATH') 59 cmd = ['ssh'] + siteconfig.get('SSH_ARGS').split() + [ssh_dest, remotecmd] 60 debug.verbose('spawning ipbenchd on %s' % host) 61 return subprocess.Popen(cmd) 62 63 def _cleanup_ipbenchd(self, user, host): 64 # run a remote killall to get rid of ipbenchd 65 ssh_dest = '%s@%s' % (user, host) 66 remotecmd = 'killall -q python' 67 cmd = ['ssh'] + siteconfig.get('SSH_ARGS').split() + [ssh_dest, remotecmd] 68 debug.verbose('killing ipbenchd on %s' % host) 69 retcode = subprocess.call(cmd) 70 if retcode != 0: 71 debug.warning('failed to killall python on %s!' % host) 72 73 def _run_ipbench(self, args, logfile): 74 cmd = [siteconfig.get('IPBENCH_PATH')] + args 75 firstrun = True 76 for _ in range(IPBENCH_ITERATIONS): 77 if firstrun: 78 firstrun = False 79 else: 80 # sleep a moment to let things settle down between runs 81 debug.verbose('sleeping between ipbench runs') 82 time.sleep(IPBENCH_SLEEPTIME) 83 84 debug.verbose('running ipbench: %s' % ' '.join(cmd)) 85 child = subprocess.Popen(cmd, stdout=subprocess.PIPE) 86 timeout = datetime.datetime.now() + IPBENCH_TIMEOUT 87 while True: 88 # wait for some output 89 (rlist, _, _) = select_timeout(timeout, [child.stdout]) 90 if not rlist: 91 debug.warning('ipbench run timed out') 92 child.terminate() 93 child.wait() 94 raise TimeoutError('waiting for ipbench') 95 # read one char at a time to avoid blocking 96 c = child.stdout.read(1) 97 if c == '': 98 break # EOF 99 logfile.write(c) 100 child.wait() 101 assert(child.returncode == 0) # check for successful exit 102 103 def run_test(self, targetip): 104 ipbenchds = [] 105 ipbenchd_hosts = [] 106 logfile = open(os.path.join(self.testdir, LOGFILENAME), 'w') 107 try: 108 # spawn ipbenchds 109 for _ in range(IPBENCH_NDAEMONS): 110 user, host = siteconfig.site.get_load_generator() 111 # can't run multiple ipbenchds on the same host 112 assert(host not in [h for (_,h) in ipbenchd_hosts]) 113 ipbenchd_hosts.append((user, host)) 114 ipbenchds.append(self._run_ipbenchd(user, host)) 115 116 # give them a moment to start 117 time.sleep(1) 118 119 # construct command-line args to ipbench, and run it 120 test, testargs = self.get_ipbench_test() 121 args = (['--test=%s' % test, '--test-args=%s' % ','.join(testargs), 122 '--test-target=%s' % targetip] 123 + ['--client=%s' % h for (_, h) in ipbenchd_hosts]) 124 self._run_ipbench(args, logfile) 125 126 finally: 127 logfile.close() 128 129 # terminate ipbenchds 130 for child in ipbenchds: 131 if child.poll() is None: 132 child.terminate() 133 child.wait() 134 for (user, host) in ipbenchd_hosts: 135 self._cleanup_ipbenchd(user, host) 136 137 def process_data(self, testdir, raw_iter): 138 cols = ('Requested Throughput,Achieved Throughput,Sent Throughput,' 139 'Packet Size,Min,Avg,Max,Standard Deviation,Median') 140 results = RowResults(cols.split(',')) 141 with open(os.path.join(testdir, LOGFILENAME), 'r') as logfile: 142 for line in logfile: 143 m = re.match('(\d+),(\d+),(\d+),(\d+),(\d+),(\d+),(\d+),' 144 '(\d+\.\d+),(\d+)', line) 145 assert(m) # line must match, otherwise we have junk output 146 vals = [float(s) if '.' in s else int(s) for s in m.groups()] 147 results.add_row(vals) 148 return results 149 150@tests.add_test 151class UDPEchoTest(EchoTestCommon): 152 '''UDP echo throughput''' 153 name = "udp_echo" 154 155 def get_ipbench_test(self): 156 (test, args) = super(UDPEchoTest, self).get_ipbench_test() 157 args.append('socktype=udp') 158 return (test, args) 159 160@tests.add_test 161class TCPEchoTest(EchoTestCommon): 162 '''TCP echo throughput''' 163 name = "tcp_echo" 164 165 def get_ipbench_test(self): 166 (test, args) = super(TCPEchoTest, self).get_ipbench_test() 167 args.append('socktype=tcp') 168 return (test, args) 169