You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
393 lines
14 KiB
Python
393 lines
14 KiB
Python
#!/usr/bin/env python
|
|
|
|
"""
|
|
Tests for the Mininet Walkthrough
|
|
|
|
TODO: missing xterm test
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import unittest
|
|
|
|
from sys import stdout
|
|
|
|
from mininet.util import quietRun, pexpect, StrictVersion
|
|
from mininet.clean import cleanup
|
|
|
|
|
|
def tsharkVersion():
|
|
"Return tshark version"
|
|
versionStr = quietRun( 'tshark -v' )
|
|
versionMatch = re.findall( r'TShark[^\d]*(\d+.\d+.\d+)', versionStr )
|
|
return versionMatch[ 0 ]
|
|
|
|
# pylint doesn't understand pexpect.match, unfortunately!
|
|
# pylint:disable=maybe-no-member
|
|
|
|
class testWalkthrough( unittest.TestCase ):
|
|
"Test Mininet walkthrough"
|
|
|
|
prompt = 'mininet>'
|
|
|
|
@staticmethod
|
|
def setup():
|
|
"Be paranoid and run cleanup() before each test"
|
|
cleanup()
|
|
|
|
# PART 1
|
|
def testHelp( self ):
|
|
"Check the usage message"
|
|
p = pexpect.spawn( 'mn -h' )
|
|
index = p.expect( [ 'Usage: mn', pexpect.EOF ] )
|
|
self.assertEqual( index, 0 )
|
|
|
|
def testWireshark( self ):
|
|
"Use tshark to test the of dissector"
|
|
# Satisfy pylint
|
|
assert self
|
|
if StrictVersion( tsharkVersion() ) < StrictVersion( '1.12.0' ):
|
|
tshark = pexpect.spawn( 'tshark -i lo -R of' )
|
|
else:
|
|
tshark = pexpect.spawn( 'tshark -i lo -Y openflow_v1' )
|
|
tshark.expect( [ 'Capturing on lo', "Capturing on 'Loopback" ] )
|
|
mn = pexpect.spawn( 'mn --test pingall' )
|
|
mn.expect( '0% dropped' )
|
|
tshark.expect( [ '74 Hello', '74 of_hello', '74 Type: OFPT_HELLO' ] )
|
|
tshark.sendintr()
|
|
mn.expect( pexpect.EOF )
|
|
tshark.expect( 'aptured' ) # 'xx packets captured'
|
|
tshark.expect( pexpect.EOF )
|
|
|
|
def testBasic( self ):
|
|
"Test basic CLI commands (help, nodes, net, dump)"
|
|
p = pexpect.spawn( 'mn -w' )
|
|
p.expect( self.prompt )
|
|
# help command
|
|
p.sendline( 'help' )
|
|
index = p.expect( [ 'commands', self.prompt ] )
|
|
self.assertEqual( index, 0, 'No output for "help" command')
|
|
# nodes command
|
|
p.sendline( 'nodes' )
|
|
p.expect( r'([chs]\d ?){4}' )
|
|
nodes = p.match.group( 0 ).split()
|
|
self.assertEqual( len( nodes ), 4, 'No nodes in "nodes" command')
|
|
p.expect( self.prompt )
|
|
# net command
|
|
p.sendline( 'net' )
|
|
expected = list( nodes )
|
|
while len( expected ) > 0:
|
|
index = p.expect( expected )
|
|
node = p.match.group( 0 )
|
|
expected.remove( node )
|
|
p.expect( '\n' )
|
|
self.assertEqual( len( expected ), 0, '"nodes" and "net" differ')
|
|
p.expect( self.prompt )
|
|
# dump command
|
|
p.sendline( 'dump' )
|
|
expected = [ r'<\w+ (%s)' % n for n in nodes ]
|
|
actual = []
|
|
for _ in nodes:
|
|
index = p.expect( expected )
|
|
node = p.match.group( 1 )
|
|
actual.append( node )
|
|
p.expect( '\n' )
|
|
self.assertEqual( actual.sort(), nodes.sort(),
|
|
'"nodes" and "dump" differ' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'exit' )
|
|
p.wait()
|
|
|
|
def testHostCommands( self ):
|
|
"Test ifconfig and ps on h1 and s1"
|
|
p = pexpect.spawn( 'mn -w' )
|
|
p.expect( self.prompt )
|
|
# Third pattern is a local interface beginning with 'eth' or 'en'
|
|
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
|
|
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
|
|
self.prompt ]
|
|
# h1 ifconfig
|
|
p.sendline( 'h1 ifconfig -a' )
|
|
ifcount = 0
|
|
while True:
|
|
index = p.expect( interfaces )
|
|
if index in (0, 3):
|
|
ifcount += 1
|
|
elif index == 1:
|
|
self.fail( 's1 interface displayed in "h1 ifconfig"' )
|
|
elif index == 2:
|
|
self.fail( 'eth0 displayed in "h1 ifconfig"' )
|
|
else:
|
|
break
|
|
self.assertEqual( ifcount, 2, 'Missing interfaces on h1')
|
|
# s1 ifconfig
|
|
p.sendline( 's1 ifconfig -a' )
|
|
ifcount = 0
|
|
while True:
|
|
index = p.expect( interfaces )
|
|
if index == 0:
|
|
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
|
|
elif index in (1, 2, 3):
|
|
ifcount += 1
|
|
else:
|
|
break
|
|
self.assertTrue( ifcount >= 3, 'Missing interfaces on s1')
|
|
# h1 ps
|
|
p.sendline( "h1 ps -a | egrep -v 'ps|grep'" )
|
|
p.expect( self.prompt )
|
|
h1Output = p.before
|
|
# s1 ps
|
|
p.sendline( "s1 ps -a | egrep -v 'ps|grep'" )
|
|
p.expect( self.prompt )
|
|
s1Output = p.before
|
|
# strip command from ps output and compute diffs
|
|
h1Output = h1Output.split( '\n' )[ 1: ]
|
|
s1Output = s1Output.split( '\n' )[ 1: ]
|
|
diffs = set( h1Output ).difference( set( s1Output ) )
|
|
# allow up to two diffs to account for daemons, etc.
|
|
self.assertTrue( len( diffs ) <= 2,
|
|
'h1 and s1 "ps" output differ too much: %s' % diffs )
|
|
p.sendline( 'exit' )
|
|
p.wait()
|
|
|
|
def testConnectivity( self ):
|
|
"Test ping and pingall"
|
|
p = pexpect.spawn( 'mn -w' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'h1 ping -c 1 h2' )
|
|
p.expect( '1 packets transmitted, 1 received' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'pingall' )
|
|
p.expect( '0% dropped' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'exit' )
|
|
p.wait()
|
|
|
|
def testSimpleHTTP( self ):
|
|
"Start an HTTP server on h1 and wget from h2"
|
|
if 'Python 2' in quietRun( 'python --version' ):
|
|
httpserver = 'SimpleHTTPServer'
|
|
else:
|
|
httpserver = 'http.server'
|
|
p = pexpect.spawn( 'mn -w', logfile=stdout )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'h1 python -m %s 80 >& /dev/null &' % httpserver )
|
|
p.expect( self.prompt )
|
|
# The walkthrough doesn't specify a delay here, and
|
|
# we also don't read the output (also a possible problem),
|
|
# but for now let's wait a number of seconds to make
|
|
# it less likely to fail due to the race condition.
|
|
p.sendline( 'px from mininet.util import waitListening;'
|
|
'waitListening(h1, port=80, timeout=30)' )
|
|
p.expect( self.prompt )
|
|
p.sendline( ' h2 wget -O - h1' )
|
|
p.expect( '200 OK' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'h1 kill %python' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'exit' )
|
|
p.wait()
|
|
|
|
# PART 2
|
|
def testRegressionRun( self ):
|
|
"Test pingpair (0% drop) and iperf (bw > 0) regression tests"
|
|
# test pingpair
|
|
p = pexpect.spawn( 'mn --test pingpair' )
|
|
p.expect( '0% dropped' )
|
|
p.expect( pexpect.EOF )
|
|
# test iperf
|
|
p = pexpect.spawn( 'mn --test iperf' )
|
|
p.expect( r"Results: \['([\d\.]+) .bits/sec'," )
|
|
bw = float( p.match.group( 1 ) )
|
|
self.assertTrue( bw > 0 )
|
|
p.expect( pexpect.EOF )
|
|
|
|
def testTopoChange( self ):
|
|
"Test pingall on single,3 and linear,4 topos"
|
|
# testing single,3
|
|
p = pexpect.spawn( 'mn --test pingall --topo single,3' )
|
|
p.expect( r'(\d+)/(\d+) received')
|
|
received = int( p.match.group( 1 ) )
|
|
sent = int( p.match.group( 2 ) )
|
|
self.assertEqual( sent, 6, 'Wrong number of pings sent in single,3' )
|
|
self.assertEqual( sent, received, 'Dropped packets in single,3')
|
|
p.expect( pexpect.EOF )
|
|
# testing linear,4
|
|
p = pexpect.spawn( 'mn --test pingall --topo linear,4' )
|
|
p.expect( r'(\d+)/(\d+) received')
|
|
received = int( p.match.group( 1 ) )
|
|
sent = int( p.match.group( 2 ) )
|
|
self.assertEqual( sent, 12, 'Wrong number of pings sent in linear,4' )
|
|
self.assertEqual( sent, received, 'Dropped packets in linear,4')
|
|
p.expect( pexpect.EOF )
|
|
|
|
def testLinkChange( self ):
|
|
"Test TCLink bw and delay"
|
|
p = pexpect.spawn( 'mn -w --link tc,bw=10,delay=10ms' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'h1 route && ping -c1 h2' )
|
|
# test bw
|
|
p.expect( self.prompt )
|
|
p.sendline( 'iperf' )
|
|
p.expect( r"Results: \['([\d\.]+) Mbits/sec'," )
|
|
bw = float( p.match.group( 1 ) )
|
|
self.assertTrue( bw < 10.1, 'Bandwidth %.2f >= 10.1 Mb/s' % bw )
|
|
self.assertTrue( bw > 9.0, 'Bandwidth %.2f <= 9 Mb/s' % bw )
|
|
p.expect( self.prompt )
|
|
# test delay
|
|
p.sendline( 'h1 ping -c 4 h2' )
|
|
p.expect( r'rtt min/avg/max/mdev = '
|
|
r'([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
|
|
delay = float( p.match.group( 2 ) )
|
|
self.assertTrue( delay >= 40, 'Delay < 40ms' )
|
|
self.assertTrue( delay <= 50, 'Delay > 50ms' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'exit' )
|
|
p.wait()
|
|
|
|
def testVerbosity( self ):
|
|
"Test debug and output verbosity"
|
|
# test output
|
|
p = pexpect.spawn( 'mn -v output' )
|
|
p.expect( self.prompt )
|
|
self.assertEqual( len( p.before ), 0, 'Too much output for "output"' )
|
|
p.sendline( 'exit' )
|
|
p.wait()
|
|
# test debug
|
|
p = pexpect.spawn( 'mn -v debug --test none' )
|
|
p.expect( pexpect.EOF )
|
|
lines = p.before.split( '\n' )
|
|
self.assertTrue( len( lines ) > 70, "Debug output is too short" )
|
|
|
|
def testCustomTopo( self ):
|
|
"Start Mininet using a custom topo, then run pingall"
|
|
# Satisfy pylint
|
|
assert self
|
|
custom = os.path.dirname( os.path.realpath( __file__ ) )
|
|
custom = os.path.join( custom, '../../custom/topo-2sw-2host.py' )
|
|
custom = os.path.normpath( custom )
|
|
p = pexpect.spawn(
|
|
'mn --custom %s --topo mytopo --test pingall' % custom )
|
|
p.expect( '0% dropped' )
|
|
p.expect( pexpect.EOF )
|
|
|
|
def testStaticMAC( self ):
|
|
"Verify that MACs are set to easy to read numbers"
|
|
p = pexpect.spawn( 'mn --mac' )
|
|
p.expect( self.prompt )
|
|
for i in range( 1, 3 ):
|
|
p.sendline( 'h%d ifconfig' % i )
|
|
p.expect( r'\s00:00:00:00:00:0%d\s' % i )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'exit' )
|
|
p.expect( pexpect.EOF )
|
|
|
|
def testSwitches( self ):
|
|
"Run iperf test using user and ovsk switches"
|
|
switches = [ 'user', 'ovsk' ]
|
|
for sw in switches:
|
|
p = pexpect.spawn( 'mn --switch %s --test iperf' % sw )
|
|
p.expect( r"Results: \['([\d\.]+) .bits/sec'," )
|
|
bw = float( p.match.group( 1 ) )
|
|
self.assertTrue( bw > 0 )
|
|
p.expect( pexpect.EOF )
|
|
|
|
def testBenchmark( self ):
|
|
"Run benchmark and verify that it takes less than 2 seconds"
|
|
p = pexpect.spawn( 'mn --test none' )
|
|
p.expect( r'completed in ([\d\.]+) seconds' )
|
|
time = float( p.match.group( 1 ) )
|
|
self.assertTrue( time < 2, 'Benchmark takes more than 2 seconds' )
|
|
|
|
def testOwnNamespace( self ):
|
|
"Test running user switch in its own namespace"
|
|
p = pexpect.spawn( 'mn --innamespace --switch user' )
|
|
p.expect( self.prompt )
|
|
interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
|
|
r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
|
|
self.prompt ]
|
|
p.sendline( 's1 ifconfig -a' )
|
|
ifcount = 0
|
|
while True:
|
|
index = p.expect( interfaces )
|
|
if index in (1, 3):
|
|
ifcount += 1
|
|
elif index == 0:
|
|
self.fail( 'h1 interface displayed in "s1 ifconfig"' )
|
|
elif index == 2:
|
|
self.fail( 'eth0 displayed in "s1 ifconfig"' )
|
|
else:
|
|
break
|
|
self.assertEqual( ifcount, 2, 'Missing interfaces on s1' )
|
|
# verify that all hosts a reachable
|
|
p.sendline( 'pingall' )
|
|
p.expect( r'(\d+)% dropped' )
|
|
dropped = int( p.match.group( 1 ) )
|
|
self.assertEqual( dropped, 0, 'pingall failed')
|
|
p.expect( self.prompt )
|
|
p.sendline( 'exit' )
|
|
p.wait()
|
|
|
|
# PART 3
|
|
def testPythonInterpreter( self ):
|
|
"Test py and px by checking IP for h1 and adding h3"
|
|
p = pexpect.spawn( 'mn -w' )
|
|
p.expect( self.prompt )
|
|
# test host IP
|
|
p.sendline( 'py h1.IP()' )
|
|
p.expect( '10.0.0.1' )
|
|
p.expect( self.prompt )
|
|
# test adding host
|
|
p.sendline( "px net.addHost('h3')" )
|
|
p.expect( self.prompt )
|
|
p.sendline( "px net.addLink(s1, h3)" )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'net' )
|
|
p.expect( 'h3' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'py h3.MAC()' )
|
|
p.expect( '([a-f0-9]{2}:?){6}' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'exit' )
|
|
p.wait()
|
|
|
|
def testLink( self ):
|
|
"Test link CLI command using ping"
|
|
p = pexpect.spawn( 'mn -w' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'link s1 h1 down' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'h1 ping -c 1 h2' )
|
|
p.expect( 'unreachable' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'link s1 h1 up' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'h1 ping -c 1 h2' )
|
|
p.expect( '0% packet loss' )
|
|
p.expect( self.prompt )
|
|
p.sendline( 'exit' )
|
|
p.wait()
|
|
|
|
@unittest.skipUnless( os.path.exists( '/tmp/pox' ) or
|
|
'1 received' in quietRun( 'ping -c 1 github.com' ),
|
|
'Github is not reachable; cannot download Pox' )
|
|
def testRemoteController( self ):
|
|
"Test Mininet using Pox controller"
|
|
# Satisfy pylint
|
|
assert self
|
|
if not os.path.exists( '/tmp/pox' ):
|
|
p = pexpect.spawn(
|
|
'git clone https://github.com/noxrepo/pox.git /tmp/pox' )
|
|
p.expect( pexpect.EOF )
|
|
pox = pexpect.spawn( '/tmp/pox/pox.py forwarding.l2_learning' )
|
|
net = pexpect.spawn(
|
|
'mn --controller=remote,ip=127.0.0.1,port=6633 --test pingall' )
|
|
net.expect( '0% dropped' )
|
|
net.expect( pexpect.EOF )
|
|
pox.sendintr()
|
|
pox.wait()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|