12f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley#!/usr/bin/env python 22f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 3c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# 4c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# Copyright (C) 2010 The Android Open Source Project 5c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# 6c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# Licensed under the Apache License, Version 2.0 (the "License"); 7c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# you may not use this file except in compliance with the License. 8c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# You may obtain a copy of the License at 9c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# 10c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# http://www.apache.org/licenses/LICENSE-2.0 11c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# 12c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# Unless required by applicable law or agreed to in writing, software 13c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# distributed under the License is distributed on an "AS IS" BASIS, 14c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# See the License for the specific language governing permissions and 16c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# limitations under the License. 17c0beca55d290fe0b1c96d78cbbcf94b05c23f5a5Peter Qiu# 182f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 192f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wileyimport math 202f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wileyimport optparse 212f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wileyimport sched 222f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wileyimport subprocess 232f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wileyimport sys 242f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wileyimport time 252f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 262f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 272f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wileydef fxrange(start, finish, increment=1.0): 282f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley """Like xrange, but with float arguments.""" 292f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley steps = int(math.ceil(float(finish - start) / increment)) 302f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 312f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley if steps < 0: 322f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley raise ValueError 332f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 342f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley for i in xrange(steps): 352f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley yield start + i * increment 362f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 372f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 382f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wileydef hms(seconds): 392f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley hours = int(seconds / (60 * 60)) 402f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley seconds -= hours * 60 * 60 412f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley minutes = int(seconds / 60) 422f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley seconds -= minutes * 60 432f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley return '%d:%02d:%02d' % (hours, minutes, seconds) 442f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 452f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 462f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wileyclass PeriodicExperiment(object): 472f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley """Uses the scheduler to run the specified function repeatedly.""" 482f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley def __init__(self, 492f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley scheduler=None, 502f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley total_duration=8 * 60 * 60, 512f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley test_interval=60, 522f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley test_function=None): 532f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._scheduler = scheduler 542f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._total_duration = total_duration 552f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._test_interval = test_interval 562f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._test_function = test_function 572f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._start = self._scheduler.timefunc() 582f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._finish = self._start + self._total_duration 592f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 602f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley def Run(self): 612f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley for start_one in fxrange(self._start, 622f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._finish, 632f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._test_interval): 642f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley time_remaining = self._finish - start_one 652f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._scheduler.enterabs(start_one, 662f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 1, # Priority 672f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._test_function, 682f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley [time_remaining]) 692f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._scheduler.run() 702f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 712f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 722f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wileyclass ManualExperiment(object): 732f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley """Runs the experiment repeatedly, prompting for input each time.""" 742f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley def __init__(self, test_function): 752f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._test_function = test_function 762f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 772f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley def Run(self): 782f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley try: 792f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley while True: 802f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._test_function(0) # Pass in a fake time remaining 812f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley _ = raw_input('Press return to run the test again. ' 822f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 'Control-c to exit.') 832f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley except KeyboardInterrupt: 842f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley return 852f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 862f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wileyclass IperfTest(object): 872f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley def __init__(self, filename, servername, individual_length): 882f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._file = file(filename, 'a') 892f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._servername = servername 902f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._individual_length = individual_length 912f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 922f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley def Run(self, remaining): 932f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley """Run iperf, log output to file, and print.""" 942f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley iperf = ['iperf', 952f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley '--client', self._servername, 962f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley # Transfer time in seconds. 972f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley '--time', str(self._individual_length), 982f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley '--reportstyle', 'c' # CSV output 992f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley ] 1002f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley print '%s remaining. Running %s' % (hms(remaining), ' '.join(iperf)) 1012f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley result = subprocess.Popen(iperf, 1022f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley stdout=subprocess.PIPE).communicate()[0] 1032f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley print result.rstrip() 1042f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley sys.stdout.flush() 1052f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._file.write(result) 1062f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._file.flush() 1072f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 1082f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley def teardown(self): 1092f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley self._file.close() 1102f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 1112f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wileydef main(): 1122f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley default_output = 'stability-' + time.strftime('%Y-%m-%d-%H-%M-%S') 1132f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 1142f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley parser = optparse.OptionParser() 1152f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley parser.add_option('--server', default=None, 1162f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley help='Machine running the iperf server') 1172f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley parser.add_option('--test_interval', default=60 * 5, type='int', 1182f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley help='Interval (in seconds) between tests') 1192f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley parser.add_option('--individual_length', default=10, type='int', 1202f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley help='length (in seconds) of each individual test') 1212f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley parser.add_option('--total_duration', default=8 * 60 * 60, type='int', 1222f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley help='length (in seconds) for entire test') 1232f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley parser.add_option('--output', default=default_output, 1242f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley help='Output file') 1252f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley parser.add_option('--manual', default=False, action='store_true', 1262f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley help='Manual mode; wait for input between every test') 1272f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 1282f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley (options, _) = parser.parse_args() 1292f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 1302f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley if not options.server: 1312f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley print 'No server specified. Specify a server with --server=SERVER.' 1322f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley exit(2) 1332f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 1342f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley if options.individual_length > options.test_interval: 1352f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley print ('The length of a given bandwidth test must be lower than the ' 1362f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 'interval between tests') 1372f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley exit(2) 1382f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 1392f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley s = sched.scheduler(time.time, time.sleep) 1402f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 1412f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley iperf = IperfTest(filename=options.output, 1422f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley servername=options.server, 1432f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley individual_length=options.individual_length) 1442f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 1452f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley if options.manual: 1462f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley e = ManualExperiment(test_function=iperf.Run) 1472f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley else: 1482f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley e = PeriodicExperiment(scheduler=s, 1492f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley total_duration=options.total_duration, 1502f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley test_interval=options.test_interval, 1512f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley test_function=iperf.Run) 1522f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley e.Run() 1532f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley iperf.teardown() 1542f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley 1552f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wileyif __name__ == '__main__': 1562f48d9572459c2c90d68e8b017b86eb843fe2a74Christopher Wiley main() 157