From c8c3bd06ef1a7248c8195d050d8a4075d051256e Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Mon, 15 Apr 2024 20:21:43 +0200 Subject: Adding upstream version 3.16. Signed-off-by: Daniel Baumann --- contrib/Dockerfile | 15 ++++++ contrib/README.txt | 12 +++++ contrib/iperf3.gp | 36 +++++++++++++ contrib/iperf3.service | 10 ++++ contrib/iperf3_to_gnuplot.py | 124 +++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 197 insertions(+) create mode 100644 contrib/Dockerfile create mode 100644 contrib/README.txt create mode 100644 contrib/iperf3.gp create mode 100644 contrib/iperf3.service create mode 100755 contrib/iperf3_to_gnuplot.py (limited to 'contrib') diff --git a/contrib/Dockerfile b/contrib/Dockerfile new file mode 100644 index 0000000..79f6c75 --- /dev/null +++ b/contrib/Dockerfile @@ -0,0 +1,15 @@ +# Instructions: +# - Configure for a static binary: ./configure --enable-static "LDFLAGS=--static" --disable-shared --without-openssl +# - Build: make +# - Build Docker image: docker build -t iperf3 -f contrib/Dockerfile . +# +# Example invocations: +# - Help: docker run iperf3 --help +# - Server: docker run -p 5201:5201 -it iperf3 -s +# - Client: docker run -it iperf3 -c 192.168.1.1 (note: since this is a minimal image and does not include DNS, name resolution will not work) +FROM scratch +COPY src/iperf3 /iperf3 +COPY tmp /tmp +ENTRYPOINT ["/iperf3"] +EXPOSE 5201 +CMD ["-s"] diff --git a/contrib/README.txt b/contrib/README.txt new file mode 100644 index 0000000..5a27790 --- /dev/null +++ b/contrib/README.txt @@ -0,0 +1,12 @@ + +This directory contains files that might be useful to analyze iperf3 results + +iperf3_to_gnuplot.py: converts iperf3 JSON output to format easy to plot in gnuplot + +iperf3.gp: sample gnuplot commands to plot throughput and retransmits + + +Other iperf3 related projects that might be of interest: + +https://github.com/kaihendry/iperf3chart + diff --git a/contrib/iperf3.gp b/contrib/iperf3.gp new file mode 100644 index 0000000..898422c --- /dev/null +++ b/contrib/iperf3.gp @@ -0,0 +1,36 @@ +# +# sample Gnuplot command file for iperf3 results +set term x11 +#set term png +#set term postscript landscape color +set key width -12 + +# iperf3 data fields +#start bytes bits_per_second retransmits snd_cwnd + +set output "iperf3.png" +#set output "iperf3.eps" + +#set nokey + +set grid xtics +set grid ytics +set grid linewidth 1 +set title "TCP performance: 40G to 10G host" +set xlabel "time (seconds)" +set ylabel "Bandwidth (Gbits/second)" +set xrange [0:60] +set yrange [0:15] +set ytics nomirror +set y2tics +set y2range [0:2500] +# dont plot when retransmits = 0 +set datafile missing '0' +set pointsize 1.6 + +plot "40Gto10G.old.dat" using 1:3 title '3.10 kernel' with linespoints lw 3 pt 5, \ + "40Gto10G.new.dat" using 1:3 title '4.2 kernel' with linespoints lw 3 pt 7, \ + "40Gto10G.old.dat" using 1:4 title 'retransmits' with points pt 7 axes x1y2 + +#plot "iperf3.old.dat" using 1:3 title '3.10 kernel' with linespoints lw 3 pt 5, \ +# "iperf3.new.dat" using 1:3 title '4.2 kernel' with linespoints lw 3 pt 7 diff --git a/contrib/iperf3.service b/contrib/iperf3.service new file mode 100644 index 0000000..e9cf47a --- /dev/null +++ b/contrib/iperf3.service @@ -0,0 +1,10 @@ +[Unit] +Description=iperf3 +Requires=network.target + +[Service] +ExecStart=/usr/bin/iperf3 -s +Restart=on-failure + +[Install] +WantedBy=multi-user.target diff --git a/contrib/iperf3_to_gnuplot.py b/contrib/iperf3_to_gnuplot.py new file mode 100755 index 0000000..6a1f7a0 --- /dev/null +++ b/contrib/iperf3_to_gnuplot.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python + +""" +Extract iperf data from json blob and format for gnuplot. +""" + +import json +import os +import sys + +from optparse import OptionParser + +import pprint +# for debugging, so output to stderr to keep verbose +# output out of any redirected stdout. +pp = pprint.PrettyPrinter(indent=4, stream=sys.stderr) + + +def generate_output(iperf, options): + """Do the actual formatting.""" + for i in iperf.get('intervals'): + for ii in i.get('streams'): + if options.verbose: + pp.pprint(ii) + row = '{0} {1} {2} {3} {4}\n'.format( + round(float(ii.get('start')), 4), + ii.get('bytes'), + # to Gbits/sec + round(float(ii.get('bits_per_second')) / (1000*1000*1000), 3), + ii.get('retransmits'), + round(float(ii.get('snd_cwnd')) / (1000*1000), 2) + ) + yield row + + +def summed_output(iperf, options): + """Format summed output.""" + + for i in iperf.get('intervals'): + + row_header = None + + byte = list() + bits_per_second = list() + retransmits = list() + snd_cwnd = list() + + for ii in i.get('streams'): + if options.verbose: + pp.pprint(i) + # grab the first start value + if row_header is None: + row_header = round(float(ii.get('start')), 2) + # aggregate the rest of the values + byte.append(ii.get('bytes')) + bits_per_second.append(float(ii.get('bits_per_second')) / (1000*1000*1000)) + retransmits.append(ii.get('retransmits')) + snd_cwnd.append(float(ii.get('snd_cwnd')) / (1000*1000)) + + row = '{h} {b} {bps} {r} {s}\n'.format( + h=row_header, + b=sum(byte), + bps=round(sum(bits_per_second), 3), + r=sum(retransmits), + s=round(sum(snd_cwnd) / len(snd_cwnd), 2) + ) + + yield row + + +def main(): + """Execute the read and formatting.""" + usage = '%prog [ -f FILE | -o OUT | -v ]' + parser = OptionParser(usage=usage) + parser.add_option('-f', '--file', metavar='FILE', + type='string', dest='filename', + help='Input filename.') + parser.add_option('-o', '--output', metavar='OUT', + type='string', dest='output', + help='Optional file to append output to.') + parser.add_option('-s', '--sum', + dest='summed', action='store_true', default=False, + help='Summed version of the output.') + parser.add_option('-v', '--verbose', + dest='verbose', action='store_true', default=False, + help='Verbose debug output to stderr.') + options, _ = parser.parse_args() + + if not options.filename: + parser.error('Filename is required.') + + file_path = os.path.normpath(options.filename) + + if not os.path.exists(file_path): + parser.error('{f} does not exist'.format(f=file_path)) + + with open(file_path, 'r') as fh: + data = fh.read() + + try: + iperf = json.loads(data) + except Exception as ex: # pylint: disable=broad-except + parser.error('Could not parse JSON from file (ex): {0}'.format(str(ex))) + + if options.output: + absp = os.path.abspath(options.output) + output_dir, _ = os.path.split(absp) + if not os.path.exists(output_dir): + parser.error('Output file directory path {0} does not exist'.format(output_dir)) + fh = open(absp, 'a') + else: + fh = sys.stdout + + if options.summed: + fmt = summed_output + else: + fmt = generate_output + + for i in fmt(iperf, options): + fh.write(i) + + +if __name__ == '__main__': + main() -- cgit v1.2.3