4 Test creation and pings for topologies with link and/or CPU options."""
8 from functools import partial
10 from mininet.net import Mininet
11 from mininet.node import OVSSwitch, UserSwitch, IVSSwitch
12 from mininet.node import CPULimitedHost
13 from mininet.link import TCLink
14 from mininet.topo import Topo
15 from mininet.log import setLogLevel
16 from mininet.util import quietRun
17 from mininet.clean import cleanup
19 # Number of hosts for each test
23 class SingleSwitchOptionsTopo(Topo):
24 "Single switch connected to n hosts."
25 def __init__(self, n=2, hopts=None, lopts=None):
30 Topo.__init__(self, hopts=hopts, lopts=lopts)
31 switch = self.addSwitch('s1')
33 host = self.addHost('h%s' % (h + 1))
34 self.addLink(host, switch)
36 # Tell pylint not to complain about calls to other class
37 # pylint: disable=E1101
39 class testOptionsTopoCommon( object ):
40 """Verify ability to create networks with host and link options
43 switchClass = None # overridden in subclasses
47 "Clean up if necessary"
48 if sys.exc_info != ( None, None, None ):
51 def runOptionsTopoTest( self, n, msg, hopts=None, lopts=None ):
52 "Generic topology-with-options test runner."
53 mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts,
55 host=CPULimitedHost, link=TCLink,
56 switch=self.switchClass, waitConnected=True )
57 dropped = mn.run( mn.ping )
58 hoptsStr = ', '.join( '%s: %s' % ( opt, value )
59 for opt, value in hopts.items() )
60 loptsStr = ', '.join( '%s: %s' % ( opt, value )
61 for opt, value in lopts.items() )
62 msg += ( '%s%% of pings were dropped during mininet.ping().\n'
63 'Topo = SingleSwitchTopo, %s hosts\n'
66 'host = CPULimitedHost\n'
69 % ( dropped, n, hoptsStr, loptsStr, self.switchClass ) )
71 self.assertEqual( dropped, 0, msg=msg )
73 def assertWithinTolerance( self, measured, expected, tolerance_frac, msg ):
74 """Check that a given value is within a tolerance of expected
75 tolerance_frac: less-than-1.0 value; 0.8 would yield 20% tolerance.
77 upperBound = ( float( expected ) + ( 1 - tolerance_frac ) *
79 lowerBound = float( expected ) * tolerance_frac
80 info = ( 'measured value is out of bounds\n'
81 'expected value: %s\n'
82 'measured value: %s\n'
83 'failure tolerance: %s\n'
86 % ( expected, measured, tolerance_frac,
87 upperBound, lowerBound ) )
90 self.assertGreaterEqual( float( measured ), lowerBound, msg=msg )
91 self.assertLessEqual( float( measured ), upperBound, msg=msg )
93 def testCPULimits( self ):
94 "Verify topology creation with CPU limits set for both schedulers."
96 CPU_TOLERANCE = 0.8 # CPU fraction below which test should fail
97 hopts = { 'cpu': CPU_FRACTION }
98 #self.runOptionsTopoTest( N, hopts=hopts )
100 mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
101 host=CPULimitedHost, switch=self.switchClass,
104 results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
106 hostUsage = '\n'.join( 'h%s: %s' %
108 results[ (n - 1) * 5 : (n * 5) - 1 ] )
109 for n in range( N ) )
110 hoptsStr = ', '.join( '%s: %s' % ( opt, value )
111 for opt, value in hopts.items() )
112 msg = ( '\nTesting cpu limited to %d%% of cpu per host\n'
113 'cpu usage percent per host:\n%s\n'
114 'Topo = SingleSwitchTopo, %s hosts\n'
116 'host = CPULimitedHost\n'
118 % ( CPU_FRACTION * 100, hostUsage, N, hoptsStr,
121 #divide cpu by 100 to convert from percentage to fraction
122 self.assertWithinTolerance( pct/100, CPU_FRACTION,
125 def testLinkBandwidth( self ):
126 "Verify that link bandwidths are accurate within a bound."
127 if self.switchClass is UserSwitch:
128 self.skipTest( 'UserSwitch has very poor performance -'
129 ' skipping for now' )
131 BW_TOLERANCE = 0.8 # BW fraction below which test should fail
132 # Verify ability to create limited-link topo first;
133 lopts = { 'bw': BW, 'use_htb': True }
134 # Also verify correctness of limit limitng within a bound.
135 mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
136 link=TCLink, switch=self.switchClass,
138 bw_strs = mn.run( mn.iperf, fmt='m' )
139 loptsStr = ', '.join( '%s: %s' % ( opt, value )
140 for opt, value in lopts.items() )
141 msg = ( '\nTesting link bandwidth limited to %d Mbps per link\n'
142 'iperf results[ client, server ]: %s\n'
143 'Topo = SingleSwitchTopo, %s hosts\n'
148 % ( BW, bw_strs, N, loptsStr, self.switchClass ) )
150 # On the client side, iperf doesn't wait for ACKs - it simply
151 # reports how long it took to fill up the TCP send buffer.
152 # As long as the kernel doesn't wait a long time before
153 # delivering bytes to the iperf server, its reported data rate
154 # should be close to the actual receive rate.
155 serverRate, _clientRate = bw_strs
156 bw = float( serverRate.split(' ')[0] )
157 self.assertWithinTolerance( bw, BW, BW_TOLERANCE, msg )
159 def testLinkDelay( self ):
160 "Verify that link delays are accurate within a bound."
162 DELAY_TOLERANCE = 0.8 # Delay fraction below which test should fail
164 lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True }
165 mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
166 link=TCLink, switch=self.switchClass, autoStaticArp=True,
169 for _ in range( REPS ):
170 ping_delays = mn.pingFull()
172 test_outputs = ping_delays[0]
173 # Ignore unused variables below
174 # pylint: disable=W0612
175 node, dest, ping_outputs = test_outputs
176 sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
177 pingFailMsg = 'sent %s pings, only received %s' % ( sent, received )
178 self.assertEqual( sent, received, msg=pingFailMsg )
179 # pylint: enable=W0612
180 loptsStr = ', '.join( '%s: %s' % ( opt, value )
181 for opt, value in lopts.items() )
182 msg = ( '\nTesting Link Delay of %s ms\n'
183 'ping results across 4 links:\n'
184 '(Sent, Received, rttmin, rttavg, rttmax, rttdev)\n'
186 'Topo = SingleSwitchTopo, %s hosts\n'
191 % ( DELAY_MS, ping_outputs, N, loptsStr, self.switchClass ) )
193 for rttval in [rttmin, rttavg, rttmax]:
194 # Multiply delay by 4 to cover there & back on two links
195 self.assertWithinTolerance( rttval, DELAY_MS * 4.0,
196 DELAY_TOLERANCE, msg )
198 def testLinkLoss( self ):
199 "Verify that we see packet drops with a high configured loss rate."
202 lopts = { 'loss': LOSS_PERCENT, 'use_htb': True }
203 mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ),
204 host=CPULimitedHost, link=TCLink,
205 switch=self.switchClass,
207 # Drops are probabilistic, but the chance of no dropped packets is
208 # 1 in 100 million with 4 hops for a link w/99% loss.
211 for _ in range(REPS):
212 dropped_total += mn.ping(timeout='1')
215 loptsStr = ', '.join( '%s: %s' % ( opt, value )
216 for opt, value in lopts.items() )
217 msg = ( '\nTesting packet loss with %d%% loss rate\n'
218 'number of dropped pings during mininet.ping(): %s\n'
219 'expected number of dropped packets: 1\n'
220 'Topo = SingleSwitchTopo, %s hosts\n'
225 % ( LOSS_PERCENT, dropped_total, N, loptsStr,
228 self.assertGreater( dropped_total, 0, msg )
230 def testMostOptions( self ):
231 "Verify topology creation with most link options and CPU limits."
232 lopts = { 'bw': 10, 'delay': '5ms', 'use_htb': True }
233 hopts = { 'cpu': 0.5 / N }
234 msg = '\nTesting many cpu and link options\n'
235 self.runOptionsTopoTest( N, msg, hopts=hopts, lopts=lopts )
237 # pylint: enable=E1101
239 class testOptionsTopoOVSKernel( testOptionsTopoCommon, unittest.TestCase ):
240 """Verify ability to create networks with host and link options
241 (OVS kernel switch)."""
243 switchClass = OVSSwitch
245 @unittest.skip( 'Skipping OVS user switch test for now' )
246 class testOptionsTopoOVSUser( testOptionsTopoCommon, unittest.TestCase ):
247 """Verify ability to create networks with host and link options
248 (OVS user switch)."""
250 switchClass = partial( OVSSwitch, datapath='user' )
252 @unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
253 class testOptionsTopoIVS( testOptionsTopoCommon, unittest.TestCase ):
254 "Verify ability to create networks with host and link options (IVS)."
256 switchClass = IVSSwitch
258 @unittest.skipUnless( quietRun( 'which ofprotocol' ),
259 'Reference user switch is not installed' )
260 class testOptionsTopoUserspace( testOptionsTopoCommon, unittest.TestCase ):
261 """Verify ability to create networks with host and link options
264 switchClass = UserSwitch
266 if __name__ == '__main__':
267 setLogLevel( 'warning' )