xref: /iperf/contrib/iperf3_to_gnuplot.py (revision 471aa5f1)
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