1c50b5ea8SBrian Tierney#!/usr/bin/env python 2c50b5ea8SBrian Tierney 3c50b5ea8SBrian Tierney""" 4c50b5ea8SBrian TierneyExtract iperf data from json blob and format for gnuplot. 5c50b5ea8SBrian Tierney""" 6c50b5ea8SBrian Tierney 7c50b5ea8SBrian Tierneyimport json 8c50b5ea8SBrian Tierneyimport os 9c50b5ea8SBrian Tierneyimport sys 10c50b5ea8SBrian Tierney 11c50b5ea8SBrian Tierneyfrom optparse import OptionParser 12c50b5ea8SBrian Tierney 13c50b5ea8SBrian Tierneyimport pprint 14c50b5ea8SBrian Tierney# for debugging, so output to stderr to keep verbose 15c50b5ea8SBrian Tierney# output out of any redirected stdout. 16c50b5ea8SBrian Tierneypp = pprint.PrettyPrinter(indent=4, stream=sys.stderr) 17c50b5ea8SBrian Tierney 182d3eb211SMonte Goode 19c50b5ea8SBrian Tierneydef generate_output(iperf, options): 202d3eb211SMonte Goode """Do the actual formatting.""" 21c50b5ea8SBrian Tierney for i in iperf.get('intervals'): 22c50b5ea8SBrian Tierney for ii in i.get('streams'): 232d3eb211SMonte Goode if options.verbose: 242d3eb211SMonte Goode pp.pprint(ii) 25c50b5ea8SBrian Tierney row = '{0} {1} {2} {3} {4}\n'.format( 26c50b5ea8SBrian Tierney round(float(ii.get('start')), 4), 27c50b5ea8SBrian Tierney ii.get('bytes'), 28c50b5ea8SBrian Tierney # to Gbits/sec 29c50b5ea8SBrian Tierney round(float(ii.get('bits_per_second')) / (1000*1000*1000), 3), 30c50b5ea8SBrian Tierney ii.get('retransmits'), 31c50b5ea8SBrian Tierney round(float(ii.get('snd_cwnd')) / (1000*1000), 2) 32c50b5ea8SBrian Tierney ) 33c50b5ea8SBrian Tierney yield row 34c50b5ea8SBrian Tierney 352d3eb211SMonte Goode 36471aa5f1SMonte Goodedef summed_output(iperf, options): 37471aa5f1SMonte Goode """Format summed output.""" 38471aa5f1SMonte Goode 39*897687acSMonte Goode for i in iperf.get('intervals'): 40*897687acSMonte Goode 418d94dc28SMonte Goode row_header = None 42471aa5f1SMonte Goode 43471aa5f1SMonte Goode byte = list() 44471aa5f1SMonte Goode bits_per_second = list() 45471aa5f1SMonte Goode retransmits = list() 46471aa5f1SMonte Goode snd_cwnd = list() 47471aa5f1SMonte Goode 48471aa5f1SMonte Goode for ii in i.get('streams'): 49471aa5f1SMonte Goode if options.verbose: 50471aa5f1SMonte Goode pp.pprint(i) 518d94dc28SMonte Goode # grab the first start value 528d94dc28SMonte Goode if row_header is None: 53*897687acSMonte Goode row_header = round(float(ii.get('start')), 2) 548d94dc28SMonte Goode # aggregate the rest of the values 55471aa5f1SMonte Goode byte.append(ii.get('bytes')) 56471aa5f1SMonte Goode bits_per_second.append(float(ii.get('bits_per_second')) / (1000*1000*1000)) 57471aa5f1SMonte Goode retransmits.append(ii.get('retransmits')) 58471aa5f1SMonte Goode snd_cwnd.append(float(ii.get('snd_cwnd')) / (1000*1000)) 59471aa5f1SMonte Goode 60471aa5f1SMonte Goode row = '{h} {b} {bps} {r} {s}\n'.format( 61471aa5f1SMonte Goode h=row_header, 62471aa5f1SMonte Goode b=sum(byte), 63471aa5f1SMonte Goode bps=round(sum(bits_per_second), 3), 64471aa5f1SMonte Goode r=sum(retransmits), 65471aa5f1SMonte Goode s=round(sum(snd_cwnd) / len(snd_cwnd), 2) 66471aa5f1SMonte Goode ) 67471aa5f1SMonte Goode 68*897687acSMonte Goode yield row 69471aa5f1SMonte Goode 70471aa5f1SMonte Goode 71c50b5ea8SBrian Tierneydef main(): 722d3eb211SMonte Goode """Execute the read and formatting.""" 73c50b5ea8SBrian Tierney usage = '%prog [ -f FILE | -o OUT | -v ]' 74c50b5ea8SBrian Tierney parser = OptionParser(usage=usage) 75c50b5ea8SBrian Tierney parser.add_option('-f', '--file', metavar='FILE', 76c50b5ea8SBrian Tierney type='string', dest='filename', 77c50b5ea8SBrian Tierney help='Input filename.') 78c50b5ea8SBrian Tierney parser.add_option('-o', '--output', metavar='OUT', 79c50b5ea8SBrian Tierney type='string', dest='output', 80c50b5ea8SBrian Tierney help='Optional file to append output to.') 81471aa5f1SMonte Goode parser.add_option('-s', '--sum', 82471aa5f1SMonte Goode dest='summed', action='store_true', default=False, 83471aa5f1SMonte Goode help='Summed version of the output.') 84c50b5ea8SBrian Tierney parser.add_option('-v', '--verbose', 85c50b5ea8SBrian Tierney dest='verbose', action='store_true', default=False, 86c50b5ea8SBrian Tierney help='Verbose debug output to stderr.') 872d3eb211SMonte Goode options, _ = parser.parse_args() 88c50b5ea8SBrian Tierney 89c50b5ea8SBrian Tierney if not options.filename: 90c50b5ea8SBrian Tierney parser.error('Filename is required.') 91c50b5ea8SBrian Tierney 92c50b5ea8SBrian Tierney file_path = os.path.normpath(options.filename) 93c50b5ea8SBrian Tierney 94c50b5ea8SBrian Tierney if not os.path.exists(file_path): 95c50b5ea8SBrian Tierney parser.error('{f} does not exist'.format(f=file_path)) 96c50b5ea8SBrian Tierney 97c50b5ea8SBrian Tierney with open(file_path, 'r') as fh: 98c50b5ea8SBrian Tierney data = fh.read() 99c50b5ea8SBrian Tierney 100c50b5ea8SBrian Tierney try: 101c50b5ea8SBrian Tierney iperf = json.loads(data) 1022d3eb211SMonte Goode except Exception as ex: # pylint: disable=broad-except 1032d3eb211SMonte Goode parser.error('Could not parse JSON from file (ex): {0}'.format(str(ex))) 104c50b5ea8SBrian Tierney 105c50b5ea8SBrian Tierney if options.output: 106c50b5ea8SBrian Tierney absp = os.path.abspath(options.output) 1072d3eb211SMonte Goode output_dir, _ = os.path.split(absp) 1082d3eb211SMonte Goode if not os.path.exists(output_dir): 1092d3eb211SMonte Goode parser.error('Output file directory path {0} does not exist'.format(output_dir)) 110c50b5ea8SBrian Tierney fh = open(absp, 'a') 111c50b5ea8SBrian Tierney else: 112c50b5ea8SBrian Tierney fh = sys.stdout 113c50b5ea8SBrian Tierney 114471aa5f1SMonte Goode if options.summed: 115471aa5f1SMonte Goode fmt = summed_output 116471aa5f1SMonte Goode else: 117471aa5f1SMonte Goode fmt = generate_output 118471aa5f1SMonte Goode 119471aa5f1SMonte Goode for i in fmt(iperf, options): 120c50b5ea8SBrian Tierney fh.write(i) 121c50b5ea8SBrian Tierney 122c50b5ea8SBrian Tierney 123c50b5ea8SBrian Tierneyif __name__ == '__main__': 124c50b5ea8SBrian Tierney main() 125