Use iperf CSV output in Mininet.iperf() (#1165)

+ identify client and server (different on Ubuntu 20 vs. 22)
+ add util.{unitScale,fmtBps} to help with output format

- internal function _parseIperfOutput has been removed
This commit is contained in:
lantz
2023-04-21 20:07:50 -07:00
committed by GitHub
parent 4707e90028
commit 30a5fd0df4
2 changed files with 62 additions and 31 deletions
+39 -31
View File
@@ -98,14 +98,14 @@ from itertools import chain, groupby
from math import ceil from math import ceil
from mininet.cli import CLI from mininet.cli import CLI
from mininet.log import info, error, debug, output, warn from mininet.log import info, error, output, warn
from mininet.node import ( Node, Host, OVSKernelSwitch, DefaultController, from mininet.node import ( Node, Host, OVSKernelSwitch, DefaultController,
Controller ) Controller )
from mininet.nodelib import NAT from mininet.nodelib import NAT
from mininet.link import Link, Intf from mininet.link import Link, Intf
from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot, from mininet.util import ( quietRun, fixLimits, numCores, ensureRoot,
macColonHex, ipStr, ipParse, netParse, ipAdd, macColonHex, ipStr, ipParse, netParse, ipAdd,
waitListening, BaseString ) waitListening, BaseString, fmtBps )
from mininet.term import cleanUpScreens, makeTerms from mininet.term import cleanUpScreens, makeTerms
# Mininet version: should be consistent with README and LICENSE # Mininet version: should be consistent with README and LICENSE
@@ -782,18 +782,25 @@ class Mininet( object ):
return self.pingFull( hosts=hosts ) return self.pingFull( hosts=hosts )
@staticmethod @staticmethod
def _parseIperf( iperfOutput ): def _iperfVals( iperfcsv, serverip, _l4Type='TCP' ):
"""Parse iperf output and return bandwidth. """Return iperf CSV as dict
iperfOutput: string iperfcsv: iperf -y C output
returns: result string""" serverip: iperf server IP address
r = r'([\d\.]+ \w+/sec)' l4Type: TCP|UDP
m = re.findall( r, iperfOutput ) """
if m: fields = 'date cip cport sip sport ipver interval sent rate'
return m[-1] lines = iperfcsv.strip().split('\n')
else: if lines:
# was: raise Exception(...) line = lines[ -1 ].split( ',' )
error( 'could not parse iperf output: ' + iperfOutput ) svals = dict( zip( fields.split(), line ) )
return '' # Return client in cip:cport, server in sip:sport
if svals[ 'cip' ] == serverip:
svals[ 'cip' ], svals[ 'sip' ] = (
svals[ 'sip' ], svals[ 'cip' ] )
svals[ 'cport' ], svals[ 'sport' ] = (
svals[ 'sport' ], svals[ 'cport' ] )
return svals
return {}
# XXX This should be cleaned up # XXX This should be cleaned up
@@ -803,7 +810,7 @@ class Mininet( object ):
hosts: list of hosts; if None, uses first and last hosts hosts: list of hosts; if None, uses first and last hosts
l4Type: string, one of [ TCP, UDP ] l4Type: string, one of [ TCP, UDP ]
udpBw: bandwidth target for UDP test udpBw: bandwidth target for UDP test
fmt: iperf format argument if any fmt: scale/format argument (e.g. m/M for Mbps)
seconds: iperf time to transmit seconds: iperf time to transmit
port: iperf port port: iperf port
returns: two-element array of [ server, client ] speeds returns: two-element array of [ server, client ] speeds
@@ -816,33 +823,34 @@ class Mininet( object ):
output( '*** Iperf: testing', l4Type, 'bandwidth between', output( '*** Iperf: testing', l4Type, 'bandwidth between',
client, 'and', server, '\n' ) client, 'and', server, '\n' )
server.cmd( 'killall -9 iperf' ) server.cmd( 'killall -9 iperf' )
iperfArgs = 'iperf -p %d ' % port # Note: CSV mode
iperfArgs = 'iperf -y C -p %d ' % port
bwArgs = '' bwArgs = ''
if l4Type == 'UDP': if l4Type == 'UDP':
iperfArgs += '-u ' iperfArgs += '-u '
bwArgs = '-b ' + udpBw + ' ' bwArgs = '-b ' + udpBw + ' '
elif l4Type != 'TCP':
raise Exception( 'Unexpected l4 type: %s' % l4Type )
if fmt:
iperfArgs += '-f %s ' % fmt
server.sendCmd( iperfArgs + '-s' ) server.sendCmd( iperfArgs + '-s' )
serverip = server.IP()
if l4Type == 'TCP': if l4Type == 'TCP':
if not waitListening( client, server.IP(), port ): if not waitListening( client, serverip, port ):
raise Exception( 'Could not connect to iperf on port %d' raise Exception( 'Could not connect to iperf on port %d'
% port ) % port )
cliout = client.cmd( iperfArgs + '-t %d -c ' % seconds + cliout = client.cmd( iperfArgs + '-t %d -c ' % seconds +
server.IP() + ' ' + bwArgs ) server.IP() + ' ' + bwArgs )
debug( 'Client output: %s\n' % cliout ) cvals = self._iperfVals( cliout, serverip )
servout = '' serverout = ''
# We want the last *b/sec from the iperf server output # Wait for output from the client session
# for TCP, there are two of them because of waitListening while True:
count = 2 if l4Type == 'TCP' else 1 serverout += server.monitor( timeoutms=5000 )
while len( re.findall( '/sec', servout ) ) < count: svals = self._iperfVals( serverout, serverip )
servout += server.monitor( timeoutms=5000 ) # Check for the client's source/output port
if ( svals and cvals[ 'sport' ] == svals[ 'sport' ]
and int( svals[ 'rate' ] ) > 0 ):
break
server.sendInt() server.sendInt()
servout += server.waitOutput() serverout += server.waitOutput()
debug( 'Server output: %s\n' % servout ) result = [ fmtBps( svals[ 'rate'], fmt ),
result = [ self._parseIperf( servout ), self._parseIperf( cliout ) ] fmtBps( cvals[ 'rate' ], fmt ) ]
if l4Type == 'UDP': if l4Type == 'UDP':
result.insert( 0, udpBw ) result.insert( 0, udpBw )
output( '*** Results: %s\n' % result ) output( '*** Results: %s\n' % result )
+23
View File
@@ -705,3 +705,26 @@ def waitListening( client=None, server='127.0.0.1', port=80, timeout=None ):
time += .5 time += .5
result = runCmd( cmd ) result = runCmd( cmd )
return True return True
def unitScale( num, prefix='' ):
"Return unit scale prefix and factor"
scale = 'kMGTP'
if prefix:
pos = scale.lower().index( prefix.lower() )
return prefix, float( 10**(3*(pos+1)) )
num, prefix, factor = float( num ), '', 1
for i, c in enumerate(scale, start=1):
f = 10**(3*i)
if num < f:
break
prefix, factor = c, f
return prefix, float( factor )
def fmtBps( bps, prefix='', fmt='%.1f %sbits/sec' ):
"""Return bps as iperf-style formatted rate string
prefix: lock to specific prefix (k, M, G, ...)
fmt: default format string for bps, prefix"""
bps = float( bps )
prefix, factor = unitScale( bps, prefix )
bps /= factor
return fmt % ( bps, prefix)