second try
[vsorcdistro/.git] / mininet / mininet / test / test_walkthrough.py
1 #!/usr/bin/env python
2
3 """
4 Tests for the Mininet Walkthrough
5
6 TODO: missing xterm test
7 """
8
9 import unittest
10 import os
11 import re
12 from mininet.util import quietRun, pexpect
13 from distutils.version import StrictVersion
14 from time import sleep
15
16
17 def tsharkVersion():
18     "Return tshark version"
19     versionStr = quietRun( 'tshark -v' )
20     versionMatch = re.findall( r'TShark[^\d]*(\d+.\d+.\d+)', versionStr )
21     return versionMatch[ 0 ]
22
23 # pylint doesn't understand pexpect.match, unfortunately!
24 # pylint:disable=maybe-no-member
25
26 class testWalkthrough( unittest.TestCase ):
27     "Test Mininet walkthrough"
28
29     prompt = 'mininet>'
30
31     # PART 1
32     def testHelp( self ):
33         "Check the usage message"
34         p = pexpect.spawn( 'mn -h' )
35         index = p.expect( [ 'Usage: mn', pexpect.EOF ] )
36         self.assertEqual( index, 0 )
37
38     def testWireshark( self ):
39         "Use tshark to test the of dissector"
40         # Satisfy pylint
41         assert self
42         if StrictVersion( tsharkVersion() ) < StrictVersion( '1.12.0' ):
43             tshark = pexpect.spawn( 'tshark -i lo -R of' )
44         else:
45             tshark = pexpect.spawn( 'tshark -i lo -Y openflow_v1' )
46         tshark.expect( [ 'Capturing on lo', "Capturing on 'Loopback'" ] )
47         mn = pexpect.spawn( 'mn --test pingall' )
48         mn.expect( '0% dropped' )
49         tshark.expect( [ '74 Hello', '74 of_hello', '74 Type: OFPT_HELLO' ] )
50         tshark.sendintr()
51         mn.expect( pexpect.EOF )
52         tshark.expect( pexpect.EOF )
53
54     def testBasic( self ):
55         "Test basic CLI commands (help, nodes, net, dump)"
56         p = pexpect.spawn( 'mn' )
57         p.expect( self.prompt )
58         # help command
59         p.sendline( 'help' )
60         index = p.expect( [ 'commands', self.prompt ] )
61         self.assertEqual( index, 0, 'No output for "help" command')
62         # nodes command
63         p.sendline( 'nodes' )
64         p.expect( r'([chs]\d ?){4}' )
65         nodes = p.match.group( 0 ).split()
66         self.assertEqual( len( nodes ), 4, 'No nodes in "nodes" command')
67         p.expect( self.prompt )
68         # net command
69         p.sendline( 'net' )
70         expected = [ x for x in nodes ]
71         while len( expected ) > 0:
72             index = p.expect( expected )
73             node = p.match.group( 0 )
74             expected.remove( node )
75             p.expect( '\n' )
76         self.assertEqual( len( expected ), 0, '"nodes" and "net" differ')
77         p.expect( self.prompt )
78         # dump command
79         p.sendline( 'dump' )
80         expected = [ r'<\w+ (%s)' % n for n in nodes ]
81         actual = []
82         for _ in nodes:
83             index = p.expect( expected )
84             node = p.match.group( 1 )
85             actual.append( node )
86             p.expect( '\n' )
87         self.assertEqual( actual.sort(), nodes.sort(),
88                           '"nodes" and "dump" differ' )
89         p.expect( self.prompt )
90         p.sendline( 'exit' )
91         p.wait()
92
93     def testHostCommands( self ):
94         "Test ifconfig and ps on h1 and s1"
95         p = pexpect.spawn( 'mn' )
96         p.expect( self.prompt )
97         # Third pattern is a local interface beginning with 'eth' or 'en'
98         interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
99                        r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
100                        self.prompt ]
101         # h1 ifconfig
102         p.sendline( 'h1 ifconfig -a' )
103         ifcount = 0
104         while True:
105             index = p.expect( interfaces )
106             if index == 0 or index == 3:
107                 ifcount += 1
108             elif index == 1:
109                 self.fail( 's1 interface displayed in "h1 ifconfig"' )
110             elif index == 2:
111                 self.fail( 'eth0 displayed in "h1 ifconfig"' )
112             else:
113                 break
114         self.assertEqual( ifcount, 2, 'Missing interfaces on h1')
115         # s1 ifconfig
116         p.sendline( 's1 ifconfig -a' )
117         ifcount = 0
118         while True:
119             index = p.expect( interfaces )
120             if index == 0:
121                 self.fail( 'h1 interface displayed in "s1 ifconfig"' )
122             elif index == 1 or index == 2 or index == 3:
123                 ifcount += 1
124             else:
125                 break
126         self.assertTrue( ifcount <= 3, 'Missing interfaces on s1')
127         # h1 ps
128         p.sendline( "h1 ps -a | egrep -v 'ps|grep'" )
129         p.expect( self.prompt )
130         h1Output = p.before
131         # s1 ps
132         p.sendline( "s1 ps -a | egrep -v 'ps|grep'" )
133         p.expect( self.prompt )
134         s1Output = p.before
135         # strip command from ps output and compute diffs
136         h1Output = h1Output.split( '\n' )[ 1: ]
137         s1Output = s1Output.split( '\n' )[ 1: ]
138         diffs = set( h1Output ).difference( set( s1Output ) )
139         # allow up to two diffs to account for daemons, etc.
140         self.assertTrue( len( diffs ) <= 2,
141                          'h1 and s1 "ps" output differ too much: %s' % diffs )
142         p.sendline( 'exit' )
143         p.wait()
144
145     def testConnectivity( self ):
146         "Test ping and pingall"
147         p = pexpect.spawn( 'mn' )
148         p.expect( self.prompt )
149         p.sendline( 'h1 ping -c 1 h2' )
150         p.expect( '1 packets transmitted, 1 received' )
151         p.expect( self.prompt )
152         p.sendline( 'pingall' )
153         p.expect( '0% dropped' )
154         p.expect( self.prompt )
155         p.sendline( 'exit' )
156         p.wait()
157
158     def testSimpleHTTP( self ):
159         "Start an HTTP server on h1 and wget from h2"
160         if 'Python 2' in quietRun( 'python --version' ):
161             httpserver = 'SimpleHTTPServer'
162         else:
163             httpserver = 'http.server'
164         p = pexpect.spawn( 'mn' )
165         p.expect( self.prompt )
166         p.sendline( 'h1 python -m %s 80 &' % httpserver )
167         # The walkthrough doesn't specify a delay here, and
168         # we also don't read the output (also a possible problem),
169         # but for now let's wait a couple of seconds to make
170         # it less likely to fail due to the race condition.
171         sleep( 2 )
172         p.expect( self.prompt )
173         p.sendline( ' h2 wget -O - h1' )
174         p.expect( '200 OK' )
175         p.expect( self.prompt )
176         p.sendline( 'h1 kill %python' )
177         p.expect( self.prompt )
178         p.sendline( 'exit' )
179         p.wait()
180
181     # PART 2
182     def testRegressionRun( self ):
183         "Test pingpair (0% drop) and iperf (bw > 0) regression tests"
184         # test pingpair
185         p = pexpect.spawn( 'mn --test pingpair' )
186         p.expect( '0% dropped' )
187         p.expect( pexpect.EOF )
188         # test iperf
189         p = pexpect.spawn( 'mn --test iperf' )
190         p.expect( r"Results: \['([\d\.]+) .bits/sec'," )
191         bw = float( p.match.group( 1 ) )
192         self.assertTrue( bw > 0 )
193         p.expect( pexpect.EOF )
194
195     def testTopoChange( self ):
196         "Test pingall on single,3 and linear,4 topos"
197         # testing single,3
198         p = pexpect.spawn( 'mn --test pingall --topo single,3' )
199         p.expect( r'(\d+)/(\d+) received')
200         received = int( p.match.group( 1 ) )
201         sent = int( p.match.group( 2 ) )
202         self.assertEqual( sent, 6, 'Wrong number of pings sent in single,3' )
203         self.assertEqual( sent, received, 'Dropped packets in single,3')
204         p.expect( pexpect.EOF )
205         # testing linear,4
206         p = pexpect.spawn( 'mn --test pingall --topo linear,4' )
207         p.expect( r'(\d+)/(\d+) received')
208         received = int( p.match.group( 1 ) )
209         sent = int( p.match.group( 2 ) )
210         self.assertEqual( sent, 12, 'Wrong number of pings sent in linear,4' )
211         self.assertEqual( sent, received, 'Dropped packets in linear,4')
212         p.expect( pexpect.EOF )
213
214     def testLinkChange( self ):
215         "Test TCLink bw and delay"
216         p = pexpect.spawn( 'mn --link tc,bw=10,delay=10ms' )
217         # test bw
218         p.expect( self.prompt )
219         p.sendline( 'iperf' )
220         p.expect( r"Results: \['([\d\.]+) Mbits/sec'," )
221         bw = float( p.match.group( 1 ) )
222         self.assertTrue( bw < 10.1, 'Bandwidth %.2f >= 10.1 Mb/s' % bw )
223         self.assertTrue( bw > 9.0, 'Bandwidth %.2f <= 9 Mb/s' % bw )
224         p.expect( self.prompt )
225         # test delay
226         p.sendline( 'h1 ping -c 4 h2' )
227         p.expect( r'rtt min/avg/max/mdev = '
228                   r'([\d\.]+)/([\d\.]+)/([\d\.]+)/([\d\.]+) ms' )
229         delay = float( p.match.group( 2 ) )
230         self.assertTrue( delay >= 40, 'Delay < 40ms' )
231         self.assertTrue( delay <= 50, 'Delay > 50ms' )
232         p.expect( self.prompt )
233         p.sendline( 'exit' )
234         p.wait()
235
236     def testVerbosity( self ):
237         "Test debug and output verbosity"
238         # test output
239         p = pexpect.spawn( 'mn -v output' )
240         p.expect( self.prompt )
241         self.assertEqual( len( p.before ), 0, 'Too much output for "output"' )
242         p.sendline( 'exit' )
243         p.wait()
244         # test debug
245         p = pexpect.spawn( 'mn -v debug --test none' )
246         p.expect( pexpect.EOF )
247         lines = p.before.split( '\n' )
248         self.assertTrue( len( lines ) > 70, "Debug output is too short" )
249
250     def testCustomTopo( self ):
251         "Start Mininet using a custom topo, then run pingall"
252         # Satisfy pylint
253         assert self
254         custom = os.path.dirname( os.path.realpath( __file__ ) )
255         custom = os.path.join( custom, '../../custom/topo-2sw-2host.py' )
256         custom = os.path.normpath( custom )
257         p = pexpect.spawn(
258             'mn --custom %s --topo mytopo --test pingall' % custom )
259         p.expect( '0% dropped' )
260         p.expect( pexpect.EOF )
261
262     def testStaticMAC( self ):
263         "Verify that MACs are set to easy to read numbers"
264         p = pexpect.spawn( 'mn --mac' )
265         p.expect( self.prompt )
266         for i in range( 1, 3 ):
267             p.sendline( 'h%d ifconfig' % i )
268             p.expect( r'\s00:00:00:00:00:0%d\s' % i )
269             p.expect( self.prompt )
270         p.sendline( 'exit' )
271         p.expect( pexpect.EOF )
272
273     def testSwitches( self ):
274         "Run iperf test using user and ovsk switches"
275         switches = [ 'user', 'ovsk' ]
276         for sw in switches:
277             p = pexpect.spawn( 'mn --switch %s --test iperf' % sw )
278             p.expect( r"Results: \['([\d\.]+) .bits/sec'," )
279             bw = float( p.match.group( 1 ) )
280             self.assertTrue( bw > 0 )
281             p.expect( pexpect.EOF )
282
283     def testBenchmark( self ):
284         "Run benchmark and verify that it takes less than 2 seconds"
285         p = pexpect.spawn( 'mn --test none' )
286         p.expect( r'completed in ([\d\.]+) seconds' )
287         time = float( p.match.group( 1 ) )
288         self.assertTrue( time < 2, 'Benchmark takes more than 2 seconds' )
289
290     def testOwnNamespace( self ):
291         "Test running user switch in its own namespace"
292         p = pexpect.spawn( 'mn --innamespace --switch user' )
293         p.expect( self.prompt )
294         interfaces = [ r'h1-eth0[:\s]', r's1-eth1[:\s]',
295                        r'[^-](eth|en)\w*\d[:\s]', r'lo[:\s]',
296                        self.prompt ]
297         p.sendline( 's1 ifconfig -a' )
298         ifcount = 0
299         while True:
300             index = p.expect( interfaces )
301             if index == 1 or index == 3:
302                 ifcount += 1
303             elif index == 0:
304                 self.fail( 'h1 interface displayed in "s1 ifconfig"' )
305             elif index == 2:
306                 self.fail( 'eth0 displayed in "s1 ifconfig"' )
307             else:
308                 break
309         self.assertEqual( ifcount, 2, 'Missing interfaces on s1' )
310         # verify that all hosts a reachable
311         p.sendline( 'pingall' )
312         p.expect( r'(\d+)% dropped' )
313         dropped = int( p.match.group( 1 ) )
314         self.assertEqual( dropped, 0, 'pingall failed')
315         p.expect( self.prompt )
316         p.sendline( 'exit' )
317         p.wait()
318
319     # PART 3
320     def testPythonInterpreter( self ):
321         "Test py and px by checking IP for h1 and adding h3"
322         p = pexpect.spawn( 'mn' )
323         p.expect( self.prompt )
324         # test host IP
325         p.sendline( 'py h1.IP()' )
326         p.expect( '10.0.0.1' )
327         p.expect( self.prompt )
328         # test adding host
329         p.sendline( "px net.addHost('h3')" )
330         p.expect( self.prompt )
331         p.sendline( "px net.addLink(s1, h3)" )
332         p.expect( self.prompt )
333         p.sendline( 'net' )
334         p.expect( 'h3' )
335         p.expect( self.prompt )
336         p.sendline( 'py h3.MAC()' )
337         p.expect( '([a-f0-9]{2}:?){6}' )
338         p.expect( self.prompt )
339         p.sendline( 'exit' )
340         p.wait()
341
342     def testLink( self ):
343         "Test link CLI command using ping"
344         p = pexpect.spawn( 'mn' )
345         p.expect( self.prompt )
346         p.sendline( 'link s1 h1 down' )
347         p.expect( self.prompt )
348         p.sendline( 'h1 ping -c 1 h2' )
349         p.expect( 'unreachable' )
350         p.expect( self.prompt )
351         p.sendline( 'link s1 h1 up' )
352         p.expect( self.prompt )
353         p.sendline( 'h1 ping -c 1 h2' )
354         p.expect( '0% packet loss' )
355         p.expect( self.prompt )
356         p.sendline( 'exit' )
357         p.wait()
358
359     @unittest.skipUnless( os.path.exists( '/tmp/pox' ) or
360                           '1 received' in quietRun( 'ping -c 1 github.com' ),
361                           'Github is not reachable; cannot download Pox' )
362     def testRemoteController( self ):
363         "Test Mininet using Pox controller"
364         # Satisfy pylint
365         assert self
366         if not os.path.exists( '/tmp/pox' ):
367             p = pexpect.spawn(
368                 'git clone https://github.com/noxrepo/pox.git /tmp/pox' )
369             p.expect( pexpect.EOF )
370         pox = pexpect.spawn( '/tmp/pox/pox.py forwarding.l2_learning' )
371         net = pexpect.spawn(
372             'mn --controller=remote,ip=127.0.0.1,port=6633 --test pingall' )
373         net.expect( '0% dropped' )
374         net.expect( pexpect.EOF )
375         pox.sendintr()
376         pox.wait()
377
378
379 if __name__ == '__main__':
380     unittest.main()