Statistics
| Branch: | Tag: | Revision:

mininet / mininet / test / test_hifi.py @ 684092ba

History | View | Annotate | Download (10.4 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
        for bw_str in bw_strs:
140
            bw = float( bw_str.split(' ')[0] )
141
            self.assertWithinTolerance( bw, BW, BW_TOLERANCE, msg )
142

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

    
177
        for rttval in [rttmin, rttavg, rttmax]:
178
            # Multiply delay by 4 to cover there & back on two links
179
            self.assertWithinTolerance( rttval, DELAY_MS * 4.0, 
180
                                        DELAY_TOLERANCE, msg )
181

    
182

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

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

    
212
        self.assertGreater( dropped_total, 0, msg )
213

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

    
221
# pylint: enable=E1101
222

    
223
class testOptionsTopoOVSKernel( testOptionsTopoCommon, unittest.TestCase ):
224
    """Verify ability to create networks with host and link options
225
       (OVS kernel switch)."""
226
    longMessage = True
227
    switchClass = OVSSwitch
228

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

    
236
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
237
class testOptionsTopoIVS( testOptionsTopoCommon, unittest.TestCase ):
238
    "Verify ability to create networks with host and link options (IVS)."
239
    longMessage = True
240
    switchClass = IVSSwitch
241

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

    
249
if __name__ == '__main__':
250
    setLogLevel( 'warning' )
251
    unittest.main()