Statistics
| Branch: | Tag: | Revision:

mininet / mininet / test / test_hifi.py @ 18aab5b7

History | View | Annotate | Download (11 KB)

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

    
199
    def testLinkLoss( self ):
200
        "Verify that we see packet drops with a high configured loss rate."
201
        LOSS_PERCENT = 99
202
        REPS = 1
203
        lopts = { 'loss': LOSS_PERCENT, 'use_htb': True }
204
        mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ),
205
                      host=CPULimitedHost, link=TCLink,
206
                      switch=self.switchClass,
207
                      waitConnected=True )
208
        # Drops are probabilistic, but the chance of no dropped packets is
209
        # 1 in 100 million with 4 hops for a link w/99% loss.
210
        dropped_total = 0
211
        mn.start()
212
        for _ in range(REPS):
213
            dropped_total += mn.ping(timeout='1')
214
        mn.stop()
215

    
216
        loptsStr = ', '.join( '%s: %s' % ( opt, value )
217
                              for opt, value in lopts.items() )
218
        msg = ( '\nTesting packet loss with %d%% loss rate\n'
219
                'number of dropped pings during mininet.ping(): %s\n'
220
                'expected number of dropped packets: 1\n'
221
                'Topo = SingleSwitchTopo, %s hosts\n'
222
                'Link = TCLink\n'
223
                'lopts = %s\n'
224
                'host = default\n'
225
                'switch = %s\n'
226
                % ( LOSS_PERCENT, dropped_total, N, loptsStr,
227
                    self.switchClass ) )
228

    
229
        self.assertGreater( dropped_total, 0, msg )
230

    
231
    def testMostOptions( self ):
232
        "Verify topology creation with most link options and CPU limits."
233
        lopts = { 'bw': 10, 'delay': '5ms', 'use_htb': True }
234
        hopts = { 'cpu': 0.5 / N }
235
        msg = '\nTesting many cpu and link options\n'
236
        self.runOptionsTopoTest( N, msg, hopts=hopts, lopts=lopts )
237

    
238
# pylint: enable=E1101
239

    
240
class testOptionsTopoOVSKernel( testOptionsTopoCommon, unittest.TestCase ):
241
    """Verify ability to create networks with host and link options
242
       (OVS kernel switch)."""
243
    longMessage = True
244
    switchClass = OVSSwitch
245

    
246
@unittest.skip( 'Skipping OVS user switch test for now' )
247
class testOptionsTopoOVSUser( testOptionsTopoCommon, unittest.TestCase ):
248
    """Verify ability to create networks with host and link options
249
       (OVS user switch)."""
250
    longMessage = True
251
    switchClass = partial( OVSSwitch, datapath='user' )
252

    
253
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
254
class testOptionsTopoIVS( testOptionsTopoCommon, unittest.TestCase ):
255
    "Verify ability to create networks with host and link options (IVS)."
256
    longMessage = True
257
    switchClass = IVSSwitch
258

    
259
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
260
                     'Reference user switch is not installed' )
261
class testOptionsTopoUserspace( testOptionsTopoCommon, unittest.TestCase ):
262
    "Verify ability to create networks with host and link options (UserSwitch)."
263
    longMessage = True
264
    switchClass = UserSwitch
265

    
266
if __name__ == '__main__':
267
    setLogLevel( 'warning' )
268
    unittest.main()