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 = None 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 # grab the first start value 51 if row_header is None: 52 row_header = round(float(ii.get('start')), 4) 53 # aggregate the rest of the values 54 byte.append(ii.get('bytes')) 55 bits_per_second.append(float(ii.get('bits_per_second')) / (1000*1000*1000)) 56 retransmits.append(ii.get('retransmits')) 57 snd_cwnd.append(float(ii.get('snd_cwnd')) / (1000*1000)) 58 59 row = '{h} {b} {bps} {r} {s}\n'.format( 60 h=row_header, 61 b=sum(byte), 62 bps=round(sum(bits_per_second), 3), 63 r=sum(retransmits), 64 s=round(sum(snd_cwnd) / len(snd_cwnd), 2) 65 ) 66 67 return row 68 69 70def main(): 71 """Execute the read and formatting.""" 72 usage = '%prog [ -f FILE | -o OUT | -v ]' 73 parser = OptionParser(usage=usage) 74 parser.add_option('-f', '--file', metavar='FILE', 75 type='string', dest='filename', 76 help='Input filename.') 77 parser.add_option('-o', '--output', metavar='OUT', 78 type='string', dest='output', 79 help='Optional file to append output to.') 80 parser.add_option('-s', '--sum', 81 dest='summed', action='store_true', default=False, 82 help='Summed version of the output.') 83 parser.add_option('-v', '--verbose', 84 dest='verbose', action='store_true', default=False, 85 help='Verbose debug output to stderr.') 86 options, _ = parser.parse_args() 87 88 if not options.filename: 89 parser.error('Filename is required.') 90 91 file_path = os.path.normpath(options.filename) 92 93 if not os.path.exists(file_path): 94 parser.error('{f} does not exist'.format(f=file_path)) 95 96 with open(file_path, 'r') as fh: 97 data = fh.read() 98 99 try: 100 iperf = json.loads(data) 101 except Exception as ex: # pylint: disable=broad-except 102 parser.error('Could not parse JSON from file (ex): {0}'.format(str(ex))) 103 104 if options.output: 105 absp = os.path.abspath(options.output) 106 output_dir, _ = os.path.split(absp) 107 if not os.path.exists(output_dir): 108 parser.error('Output file directory path {0} does not exist'.format(output_dir)) 109 fh = open(absp, 'a') 110 else: 111 fh = sys.stdout 112 113 if options.summed: 114 fmt = summed_output 115 else: 116 fmt = generate_output 117 118 for i in fmt(iperf, options): 119 fh.write(i) 120 121 122if __name__ == '__main__': 123 main() 124