Files
mininet/mininet/test/test_walkthrough.py
T
lantz cf5675876c update CI workflows: update/fix codecheck; remove Ubuntu 18.04 (#1167)
1. update/fix codecheck

Run workflow with pylint 2.15.7

Add a  bunch of fiddly and mostly cosmetic changes to
make pylint (and make codecheck) happy.

Also try to run pyflakes3 vs. pyflakes

2. remove Ubuntu 18.04

Ubuntu 18.04 has been removed from github actions, so
we remove it from our workflow as well
2023-04-21 17:37:09 -07:00

393 lines
14 KiB
Python
Executable File

#!/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()