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