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