1#!/usr/bin/env python 2 3""" 4Extract iperf data from json blob and format for gnuplot. 5""" 6 7import json 8import os 9import sys 10 11from optparse import OptionParser 12 13import pprint 14# for debugging, so output to stderr to keep verbose 15# output out of any redirected stdout. 16pp = pprint.PrettyPrinter(indent=4, stream=sys.stderr) 17 18 19def generate_output(iperf, options): 20 """Do the actual formatting.""" 21 for i in iperf.get('intervals'): 22 for ii in i.get('streams'): 23 if options.verbose: 24 pp.pprint(ii) 25 row = '{0} {1} {2} {3} {4}\n'.format( 26 round(float(ii.get('start')), 4), 27 ii.get('bytes'), 28 # to Gbits/sec 29 round(float(ii.get('bits_per_second')) / (1000*1000*1000), 3), 30 ii.get('retransmits'), 31 round(float(ii.get('snd_cwnd')) / (1000*1000), 2) 32 ) 33 yield row 34 35 36def summed_output(iperf, options): 37 """Format summed output.""" 38 39 row_header = '???' # XXX get this value 40 41 byte = list() 42 bits_per_second = list() 43 retransmits = list() 44 snd_cwnd = list() 45 46 for i in iperf.get('intervals'): 47 for ii in i.get('streams'): 48 if options.verbose: 49 pp.pprint(i) 50 byte.append(ii.get('bytes')) 51 bits_per_second.append(float(ii.get('bits_per_second')) / (1000*1000*1000)) 52 retransmits.append(ii.get('retransmits')) 53 snd_cwnd.append(float(ii.get('snd_cwnd')) / (1000*1000)) 54 55 row = '{h} {b} {bps} {r} {s}\n'.format( 56 h=row_header, 57 b=sum(byte), 58 bps=round(sum(bits_per_second), 3), 59 r=sum(retransmits), 60 s=round(sum(snd_cwnd) / len(snd_cwnd), 2) 61 ) 62 63 return row 64 65 66def main(): 67 """Execute the read and formatting.""" 68 usage = '%prog [ -f FILE | -o OUT | -v ]' 69 parser = OptionParser(usage=usage) 70 parser.add_option('-f', '--file', metavar='FILE', 71 type='string', dest='filename', 72 help='Input filename.') 73 parser.add_option('-o', '--output', metavar='OUT', 74 type='string', dest='output', 75 help='Optional file to append output to.') 76 parser.add_option('-s', '--sum', 77 dest='summed', action='store_true', default=False, 78 help='Summed version of the output.') 79 parser.add_option('-v', '--verbose', 80 dest='verbose', action='store_true', default=False, 81 help='Verbose debug output to stderr.') 82 options, _ = parser.parse_args() 83 84 if not options.filename: 85 parser.error('Filename is required.') 86 87 file_path = os.path.normpath(options.filename) 88 89 if not os.path.exists(file_path): 90 parser.error('{f} does not exist'.format(f=file_path)) 91 92 with open(file_path, 'r') as fh: 93 data = fh.read() 94 95 try: 96 iperf = json.loads(data) 97 except Exception as ex: # pylint: disable=broad-except 98 parser.error('Could not parse JSON from file (ex): {0}'.format(str(ex))) 99 100 if options.output: 101 absp = os.path.abspath(options.output) 102 output_dir, _ = os.path.split(absp) 103 if not os.path.exists(output_dir): 104 parser.error('Output file directory path {0} does not exist'.format(output_dir)) 105 fh = open(absp, 'a') 106 else: 107 fh = sys.stdout 108 109 if options.summed: 110 fmt = summed_output 111 else: 112 fmt = generate_output 113 114 for i in fmt(iperf, options): 115 fh.write(i) 116 117 118if __name__ == '__main__': 119 main() 120