Update and rename MantenerFIFO to MantenerFIFO.md
[vsorcdistro/.git] / mininet / mininet / test / test_hifi.py
1 #!/usr/bin/env python
2
3 """Package: mininet
4    Test creation and pings for topologies with link and/or CPU options."""
5
6 import unittest
7 import sys
8 from functools import partial
9
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
18
19 # Number of hosts for each test
20 N = 2
21
22
23 class SingleSwitchOptionsTopo(Topo):
24     "Single switch connected to n hosts."
25     def __init__(self, n=2, hopts=None, lopts=None):
26         if not hopts:
27             hopts = {}
28         if not lopts:
29             lopts = {}
30         Topo.__init__(self, hopts=hopts, lopts=lopts)
31         switch = self.addSwitch('s1')
32         for h in range(n):
33             host = self.addHost('h%s' % (h + 1))
34             self.addLink(host, switch)
35
36 # Tell pylint not to complain about calls to other class
37 # pylint: disable=E1101
38
39 class testOptionsTopoCommon( object ):
40     """Verify ability to create networks with host and link options
41        (common code)."""
42
43     switchClass = None  # overridden in subclasses
44
45     @staticmethod
46     def tearDown():
47         "Clean up if necessary"
48         if sys.exc_info != ( None, None, None ):
49             cleanup()
50
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,
54                                                     lopts=lopts ),
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'
64                  'hopts = %s\n'
65                  'lopts = %s\n'
66                  'host = CPULimitedHost\n'
67                  'link = TCLink\n'
68                  'Switch = %s\n'
69                  % ( dropped, n, hoptsStr, loptsStr, self.switchClass ) )
70
71         self.assertEqual( dropped, 0, msg=msg )
72
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.
76         """
77         upperBound = ( float( expected ) + ( 1 - tolerance_frac ) *
78                        float( expected ) )
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'
84                  'upper bound: %s\n'
85                  'lower bound: %s\n'
86                  % ( expected, measured, tolerance_frac,
87                      upperBound, lowerBound ) )
88         msg += info
89
90         self.assertGreaterEqual( float( measured ), lowerBound, msg=msg )
91         self.assertLessEqual( float( measured ), upperBound, msg=msg )
92
93     def testCPULimits( self ):
94         "Verify topology creation with CPU limits set for both schedulers."
95         CPU_FRACTION = 0.1
96         CPU_TOLERANCE = 0.8  # CPU fraction below which test should fail
97         hopts = { 'cpu': CPU_FRACTION }
98         #self.runOptionsTopoTest( N, hopts=hopts )
99
100         mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
101                       host=CPULimitedHost, switch=self.switchClass,
102                       waitConnected=True )
103         mn.start()
104         results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
105         mn.stop()
106         hostUsage = '\n'.join( 'h%s: %s' %
107                                ( n + 1,
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'
115                 'hopts = %s\n'
116                 'host = CPULimitedHost\n'
117                 'Switch = %s\n'
118                 % ( CPU_FRACTION * 100, hostUsage, N, hoptsStr,
119                     self.switchClass ) )
120         for pct in results:
121             #divide cpu by 100 to convert from percentage to fraction
122             self.assertWithinTolerance( pct/100, CPU_FRACTION,
123                                         CPU_TOLERANCE, msg )
124
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' )
130         BW = 5  # Mbps
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,
137                       waitConnected=True )
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'
144                 'Link = TCLink\n'
145                 'lopts = %s\n'
146                 'host = default\n'
147                 'switch = %s\n'
148                 % ( BW, bw_strs, N, loptsStr, self.switchClass ) )
149
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 )
158
159     def testLinkDelay( self ):
160         "Verify that link delays are accurate within a bound."
161         DELAY_MS = 15
162         DELAY_TOLERANCE = 0.8  # Delay fraction below which test should fail
163         REPS = 3
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,
167                       waitConnected=True )
168         mn.start()
169         for _ in range( REPS ):
170             ping_delays = mn.pingFull()
171         mn.stop()
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'
185                 '%s\n'
186                 'Topo = SingleSwitchTopo, %s hosts\n'
187                 'Link = TCLink\n'
188                 'lopts = %s\n'
189                 'host = default'
190                 'switch = %s\n'
191                 % ( DELAY_MS, ping_outputs, N, loptsStr, self.switchClass ) )
192
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 )
197
198     def testLinkLoss( self ):
199         "Verify that we see packet drops with a high configured loss rate."
200         LOSS_PERCENT = 99
201         REPS = 1
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,
206                       waitConnected=True )
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.
209         dropped_total = 0
210         mn.start()
211         for _ in range(REPS):
212             dropped_total += mn.ping(timeout='1')
213         mn.stop()
214
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'
221                 'Link = TCLink\n'
222                 'lopts = %s\n'
223                 'host = default\n'
224                 'switch = %s\n'
225                 % ( LOSS_PERCENT, dropped_total, N, loptsStr,
226                     self.switchClass ) )
227
228         self.assertGreater( dropped_total, 0, msg )
229
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 )
236
237 # pylint: enable=E1101
238
239 class testOptionsTopoOVSKernel( testOptionsTopoCommon, unittest.TestCase ):
240     """Verify ability to create networks with host and link options
241        (OVS kernel switch)."""
242     longMessage = True
243     switchClass = OVSSwitch
244
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)."""
249     longMessage = True
250     switchClass = partial( OVSSwitch, datapath='user' )
251
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)."
255     longMessage = True
256     switchClass = IVSSwitch
257
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
262      (UserSwitch)."""
263     longMessage = True
264     switchClass = UserSwitch
265
266 if __name__ == '__main__':
267     setLogLevel( 'warning' )
268     unittest.main()