Statistics
| Branch: | Tag: | Revision:

mininet / mininet / test / test_hifi.py @ e1711f35

History | View | Annotate | Download (10.8 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
from functools import partial
8

    
9
from mininet.net import Mininet
10
from mininet.node import OVSSwitch, UserSwitch, IVSSwitch
11
from mininet.node import CPULimitedHost
12
from mininet.link import TCLink
13
from mininet.topo import Topo
14
from mininet.log import setLogLevel
15
from mininet.util import quietRun
16

    
17
# Number of hosts for each test
18
N = 2
19

    
20

    
21
class SingleSwitchOptionsTopo(Topo):
22
    "Single switch connected to n hosts."
23
    def __init__(self, n=2, hopts=None, lopts=None):
24
        if not hopts:
25
            hopts = {}
26
        if not lopts:
27
            lopts = {}
28
        Topo.__init__(self, hopts=hopts, lopts=lopts)
29
        switch = self.addSwitch('s1')
30
        for h in range(n):
31
            host = self.addHost('h%s' % (h + 1))
32
            self.addLink(host, switch)
33

    
34
# Tell pylint not to complain about calls to other class
35
# pylint: disable=E1101
36

    
37
class testOptionsTopoCommon( object ):
38
    """Verify ability to create networks with host and link options
39
       (common code)."""
40

    
41
    switchClass = None # overridden in subclasses
42

    
43
    def runOptionsTopoTest( self, n, msg, hopts=None, lopts=None ):
44
        "Generic topology-with-options test runner."
45
        mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts,
46
                                                    lopts=lopts ),
47
                      host=CPULimitedHost, link=TCLink,
48
                      switch=self.switchClass, waitConnected=True )
49
        dropped = mn.run( mn.ping )
50
        hoptsStr = ', '.join( '%s: %s' % ( opt, value )
51
                              for opt, value in hopts.items() )
52
        loptsStr = ', '.join( '%s: %s' % ( opt, value )
53
                              for opt, value in lopts.items() )
54
        msg += ( '%s%% of pings were dropped during mininet.ping().\n'
55
                 'Topo = SingleSwitchTopo, %s hosts\n'
56
                 'hopts = %s\n'
57
                 'lopts = %s\n'
58
                 'host = CPULimitedHost\n'
59
                 'link = TCLink\n'
60
                 'Switch = %s\n'
61
                 % ( dropped, n, hoptsStr, loptsStr, self.switchClass ) )
62

    
63
        self.assertEqual( dropped, 0, msg=msg )
64

    
65
    def assertWithinTolerance( self, measured, expected, tolerance_frac, msg ):
66
        """Check that a given value is within a tolerance of expected
67
        tolerance_frac: less-than-1.0 value; 0.8 would yield 20% tolerance.
68
        """
69
        upperBound = ( float( expected ) + ( 1 - tolerance_frac ) *
70
                       float( expected ) )
71
        lowerBound = float( expected ) * tolerance_frac
72
        info = ( 'measured value is out of bounds\n'
73
                 'expected value: %s\n'
74
                 'measured value: %s\n'
75
                 'failure tolerance: %s\n'
76
                 'upper bound: %s\n'
77
                 'lower bound: %s\n'
78
                 % ( expected, measured, tolerance_frac,
79
                     upperBound, lowerBound ) )
80
        msg += info
81

    
82
        self.assertGreaterEqual( float( measured ),lowerBound, msg=msg )
83
        self.assertLessEqual( float( measured ), upperBound, msg=msg )
84

    
85
    def testCPULimits( self ):
86
        "Verify topology creation with CPU limits set for both schedulers."
87
        CPU_FRACTION = 0.1
88
        CPU_TOLERANCE = 0.8  # CPU fraction below which test should fail
89
        hopts = { 'cpu': CPU_FRACTION }
90
        #self.runOptionsTopoTest( N, hopts=hopts )
91

    
92
        mn = Mininet( SingleSwitchOptionsTopo( n=N, hopts=hopts ),
93
                      host=CPULimitedHost, switch=self.switchClass,
94
                      waitConnected=True )
95
        mn.start()
96
        results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
97
        mn.stop()
98
        hostUsage = '\n'.join( 'h%s: %s' %
99
                               ( n + 1, results[ ( n - 1 ) * 5: ( n * 5 ) - 1 ] )
100
                               for n in range( N ) )
101
        hoptsStr = ', '.join( '%s: %s' % ( opt, value )
102
                              for opt, value in hopts.items() )
103
        msg = ( '\nTesting cpu limited to %d%% of cpu per host\n'
104
                'cpu usage percent per host:\n%s\n'
105
                'Topo = SingleSwitchTopo, %s hosts\n'
106
                'hopts = %s\n'
107
                'host = CPULimitedHost\n'
108
                'Switch = %s\n'
109
                % ( CPU_FRACTION * 100, hostUsage, N, hoptsStr, self.switchClass ) )
110
        for pct in results:
111
            #divide cpu by 100 to convert from percentage to fraction
112
            self.assertWithinTolerance( pct/100, CPU_FRACTION,
113
                                        CPU_TOLERANCE, msg )
114

    
115
    def testLinkBandwidth( self ):
116
        "Verify that link bandwidths are accurate within a bound."
117
        if self.switchClass is UserSwitch:
118
            self.skipTest ( 'UserSwitch has very poor performance, so skip for now' )
119
        BW = 5  # Mbps
120
        BW_TOLERANCE = 0.8  # BW fraction below which test should fail
121
        # Verify ability to create limited-link topo first;
122
        lopts = { 'bw': BW, 'use_htb': True }
123
        # Also verify correctness of limit limitng within a bound.
124
        mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
125
                      link=TCLink, switch=self.switchClass,
126
                      waitConnected=True )
127
        bw_strs = mn.run( mn.iperf, format='m' )
128
        loptsStr = ', '.join( '%s: %s' % ( opt, value )
129
                              for opt, value in lopts.items() )
130
        msg = ( '\nTesting link bandwidth limited to %d Mbps per link\n'
131
                'iperf results[ client, server ]: %s\n'
132
                'Topo = SingleSwitchTopo, %s hosts\n'
133
                'Link = TCLink\n'
134
                'lopts = %s\n'
135
                'host = default\n'
136
                'switch = %s\n'
137
                % ( BW, bw_strs, N, loptsStr, self.switchClass ) )
138

    
139
        # On the client side, iperf doesn't wait for ACKs - it simply
140
        # reports how long it took to fill up the TCP send buffer.
141
        # As long as the kernel doesn't wait a long time before
142
        # delivering bytes to the iperf server, its reported data rate
143
        # should be close to the actual receive rate.
144
        serverRate, clientRate = bw_strs
145
        bw = float( serverRate.split(' ')[0] )
146
        self.assertWithinTolerance( bw, BW, BW_TOLERANCE, msg )
147

    
148
    def testLinkDelay( self ):
149
        "Verify that link delays are accurate within a bound."
150
        DELAY_MS = 15
151
        DELAY_TOLERANCE = 0.8  # Delay fraction below which test should fail
152
        REPS = 3
153
        lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True }
154
        mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
155
                      link=TCLink, switch=self.switchClass, autoStaticArp=True,
156
                      waitConnected=True )
157
        mn.start()
158
        for _ in range( REPS ):
159
            ping_delays = mn.pingFull()
160
        mn.stop()
161
        test_outputs = ping_delays[0]
162
        # Ignore unused variables below
163
        # pylint: disable-msg=W0612
164
        node, dest, ping_outputs = test_outputs
165
        sent, received, rttmin, rttavg, rttmax, rttdev = ping_outputs
166
        pingFailMsg = 'sent %s pings, only received %s' % ( sent, received )
167
        self.assertEqual( sent, received, msg=pingFailMsg )
168
        # pylint: enable-msg=W0612
169
        loptsStr = ', '.join( '%s: %s' % ( opt, value )
170
                              for opt, value in lopts.items() )
171
        msg = ( '\nTesting Link Delay of %s ms\n'
172
                'ping results across 4 links:\n'
173
                '(Sent, Received, rttmin, rttavg, rttmax, rttdev)\n'
174
                '%s\n'
175
                'Topo = SingleSwitchTopo, %s hosts\n'
176
                'Link = TCLink\n'
177
                'lopts = %s\n'
178
                'host = default'
179
                'switch = %s\n'
180
                % ( DELAY_MS, ping_outputs, N, loptsStr, self.switchClass ) )
181

    
182
        for rttval in [rttmin, rttavg, rttmax]:
183
            # Multiply delay by 4 to cover there & back on two links
184
            self.assertWithinTolerance( rttval, DELAY_MS * 4.0, 
185
                                        DELAY_TOLERANCE, msg )
186

    
187

    
188
    def testLinkLoss( self ):
189
        "Verify that we see packet drops with a high configured loss rate."
190
        LOSS_PERCENT = 99
191
        REPS = 1
192
        lopts = { 'loss': LOSS_PERCENT, 'use_htb': True }
193
        mn = Mininet( topo=SingleSwitchOptionsTopo( n=N, lopts=lopts ),
194
                      host=CPULimitedHost, link=TCLink,
195
                      switch=self.switchClass,
196
                      waitConnected=True )
197
        # Drops are probabilistic, but the chance of no dropped packets is
198
        # 1 in 100 million with 4 hops for a link w/99% loss.
199
        dropped_total = 0
200
        mn.start()
201
        for _ in range(REPS):
202
            dropped_total += mn.ping(timeout='1')
203
        mn.stop()
204

    
205
        loptsStr = ', '.join( '%s: %s' % ( opt, value )
206
                              for opt, value in lopts.items() )
207
        msg = ( '\nTesting packet loss with %d%% loss rate\n'
208
                'number of dropped pings during mininet.ping(): %s\n'
209
                'expected number of dropped packets: 1\n'
210
                'Topo = SingleSwitchTopo, %s hosts\n'
211
                'Link = TCLink\n'
212
                'lopts = %s\n'
213
                'host = default\n'
214
                'switch = %s\n'
215
                % ( LOSS_PERCENT, dropped_total, N, loptsStr, self.switchClass ) )
216

    
217
        self.assertGreater( dropped_total, 0, msg )
218

    
219
    def testMostOptions( self ):
220
        "Verify topology creation with most link options and CPU limits."
221
        lopts = { 'bw': 10, 'delay': '5ms', 'use_htb': True }
222
        hopts = { 'cpu': 0.5 / N }
223
        msg = '\nTesting many cpu and link options\n'
224
        self.runOptionsTopoTest( N, msg, hopts=hopts, lopts=lopts )
225

    
226
# pylint: enable=E1101
227

    
228
class testOptionsTopoOVSKernel( testOptionsTopoCommon, unittest.TestCase ):
229
    """Verify ability to create networks with host and link options
230
       (OVS kernel switch)."""
231
    longMessage = True
232
    switchClass = OVSSwitch
233

    
234
@unittest.skip( 'Skipping OVS user switch test for now' )
235
class testOptionsTopoOVSUser( testOptionsTopoCommon, unittest.TestCase ):
236
    """Verify ability to create networks with host and link options
237
       (OVS user switch)."""
238
    longMessage = True
239
    switchClass = partial( OVSSwitch, datapath='user' )
240

    
241
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
242
class testOptionsTopoIVS( testOptionsTopoCommon, unittest.TestCase ):
243
    "Verify ability to create networks with host and link options (IVS)."
244
    longMessage = True
245
    switchClass = IVSSwitch
246

    
247
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
248
                     'Reference user switch is not installed' )
249
class testOptionsTopoUserspace( testOptionsTopoCommon, unittest.TestCase ):
250
    "Verify ability to create networks with host and link options (UserSwitch)."
251
    longMessage = True
252
    switchClass = UserSwitch
253

    
254
if __name__ == '__main__':
255
    setLogLevel( 'warning' )
256
    unittest.main()