Statistics
| Branch: | Tag: | Revision:

mininet / mininet / test / test_hifi.py @ e1711f35

History | View | Annotate | Download (10.8 KB)

1 e1205a8a Brandon Heller
#!/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 54932125 Bob Lantz
from functools import partial
8 e1205a8a Brandon Heller
9
from mininet.net import Mininet
10 54932125 Bob Lantz
from mininet.node import OVSSwitch, UserSwitch, IVSSwitch
11 1f1d590c Brandon Heller
from mininet.node import CPULimitedHost
12
from mininet.link import TCLink
13 e1205a8a Brandon Heller
from mininet.topo import Topo
14
from mininet.log import setLogLevel
15 94324e3f Bob Lantz
from mininet.util import quietRun
16 e1205a8a Brandon Heller
17
# Number of hosts for each test
18
N = 2
19
20
21
class SingleSwitchOptionsTopo(Topo):
22
    "Single switch connected to n hosts."
23 1f1d590c Brandon Heller
    def __init__(self, n=2, hopts=None, lopts=None):
24
        if not hopts:
25
            hopts = {}
26
        if not lopts:
27
            lopts = {}
28 e1205a8a Brandon Heller
        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 9d14c841 Bob Lantz
# Tell pylint not to complain about calls to other class
35
# pylint: disable=E1101
36 e1205a8a Brandon Heller
37 0e2cc609 Rich Lane
class testOptionsTopoCommon( object ):
38 9d14c841 Bob Lantz
    """Verify ability to create networks with host and link options
39
       (common code)."""
40 0e2cc609 Rich Lane
41
    switchClass = None # overridden in subclasses
42 e1205a8a Brandon Heller
43 73adba8b cody burkard
    def runOptionsTopoTest( self, n, msg, hopts=None, lopts=None ):
44 e1205a8a Brandon Heller
        "Generic topology-with-options test runner."
45 1f1d590c Brandon Heller
        mn = Mininet( topo=SingleSwitchOptionsTopo( n=n, hopts=hopts,
46
                                                    lopts=lopts ),
47 9d14c841 Bob Lantz
                      host=CPULimitedHost, link=TCLink,
48 84c1c24c cody burkard
                      switch=self.switchClass, waitConnected=True )
49 e1205a8a Brandon Heller
        dropped = mn.run( mn.ping )
50 73adba8b cody burkard
        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 1f1d590c Brandon Heller
        """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 73adba8b cody burkard
        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 1f1d590c Brandon Heller
85 e1205a8a Brandon Heller
    def testCPULimits( self ):
86 1f1d590c Brandon Heller
        "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 84c1c24c cody burkard
                      host=CPULimitedHost, switch=self.switchClass,
94
                      waitConnected=True )
95 1f1d590c Brandon Heller
        mn.start()
96
        results = mn.runCpuLimitTest( cpu=CPU_FRACTION )
97
        mn.stop()
98 73adba8b cody burkard
        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 3131c903 Cody Burkard
        for pct in results:
111 4e76439c Cody Burkard
            #divide cpu by 100 to convert from percentage to fraction
112 73adba8b cody burkard
            self.assertWithinTolerance( pct/100, CPU_FRACTION,
113
                                        CPU_TOLERANCE, msg )
114 e1205a8a Brandon Heller
115
    def testLinkBandwidth( self ):
116 1f1d590c Brandon Heller
        "Verify that link bandwidths are accurate within a bound."
117 84c1c24c cody burkard
        if self.switchClass is UserSwitch:
118
            self.skipTest ( 'UserSwitch has very poor performance, so skip for now' )
119
        BW = 5  # Mbps
120 1f1d590c Brandon Heller
        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 84c1c24c cody burkard
                      link=TCLink, switch=self.switchClass,
126
                      waitConnected=True )
127 4e76439c Cody Burkard
        bw_strs = mn.run( mn.iperf, format='m' )
128 73adba8b cody burkard
        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 e1711f35 Bob Lantz
        # 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 e1205a8a Brandon Heller
148
    def testLinkDelay( self ):
149 1f1d590c Brandon Heller
        "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 c75ff7ec cody burkard
        REPS = 3
153 1f1d590c Brandon Heller
        lopts = { 'delay': '%sms' % DELAY_MS, 'use_htb': True }
154
        mn = Mininet( SingleSwitchOptionsTopo( n=N, lopts=lopts ),
155 84c1c24c cody burkard
                      link=TCLink, switch=self.switchClass, autoStaticArp=True,
156
                      waitConnected=True )
157 c75ff7ec cody burkard
        mn.start()
158
        for _ in range( REPS ):
159
            ping_delays = mn.pingFull()
160
        mn.stop()
161 1f1d590c Brandon Heller
        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 73adba8b cody burkard
        pingFailMsg = 'sent %s pings, only received %s' % ( sent, received )
167
        self.assertEqual( sent, received, msg=pingFailMsg )
168 1f1d590c Brandon Heller
        # pylint: enable-msg=W0612
169 73adba8b cody burkard
        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 1f1d590c Brandon Heller
        for rttval in [rttmin, rttavg, rttmax]:
183 342b743b Cody Burkard
            # Multiply delay by 4 to cover there & back on two links
184 93ddd926 Cody Burkard
            self.assertWithinTolerance( rttval, DELAY_MS * 4.0, 
185 73adba8b cody burkard
                                        DELAY_TOLERANCE, msg )
186 e1205a8a Brandon Heller
187 4e76439c Cody Burkard
188 e1205a8a Brandon Heller
    def testLinkLoss( self ):
189 1f1d590c Brandon Heller
        "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 9d14c841 Bob Lantz
                      host=CPULimitedHost, link=TCLink,
195 84c1c24c cody burkard
                      switch=self.switchClass,
196
                      waitConnected=True )
197 1f1d590c Brandon Heller
        # 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 73adba8b cody burkard
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 1f1d590c Brandon Heller
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 e1205a8a Brandon Heller
        hopts = { 'cpu': 0.5 / N }
223 73adba8b cody burkard
        msg = '\nTesting many cpu and link options\n'
224
        self.runOptionsTopoTest( N, msg, hopts=hopts, lopts=lopts )
225 e1205a8a Brandon Heller
226 9d14c841 Bob Lantz
# pylint: enable=E1101
227
228 0e2cc609 Rich Lane
class testOptionsTopoOVSKernel( testOptionsTopoCommon, unittest.TestCase ):
229 9d14c841 Bob Lantz
    """Verify ability to create networks with host and link options
230
       (OVS kernel switch)."""
231 73adba8b cody burkard
    longMessage = True
232 54932125 Bob Lantz
    switchClass = OVSSwitch
233
234
@unittest.skip( 'Skipping OVS user switch test for now' )
235
class testOptionsTopoOVSUser( testOptionsTopoCommon, unittest.TestCase ):
236 9d14c841 Bob Lantz
    """Verify ability to create networks with host and link options
237
       (OVS user switch)."""
238 73adba8b cody burkard
    longMessage = True
239 54932125 Bob Lantz
    switchClass = partial( OVSSwitch, datapath='user' )
240 0e2cc609 Rich Lane
241 94324e3f Bob Lantz
@unittest.skipUnless( quietRun( 'which ivs-ctl' ), 'IVS is not installed' )
242 0e2cc609 Rich Lane
class testOptionsTopoIVS( testOptionsTopoCommon, unittest.TestCase ):
243 9d14c841 Bob Lantz
    "Verify ability to create networks with host and link options (IVS)."
244 73adba8b cody burkard
    longMessage = True
245 0e2cc609 Rich Lane
    switchClass = IVSSwitch
246
247 94324e3f Bob Lantz
@unittest.skipUnless( quietRun( 'which ofprotocol' ),
248
                     'Reference user switch is not installed' )
249 0e2cc609 Rich Lane
class testOptionsTopoUserspace( testOptionsTopoCommon, unittest.TestCase ):
250 9d14c841 Bob Lantz
    "Verify ability to create networks with host and link options (UserSwitch)."
251 73adba8b cody burkard
    longMessage = True
252 0e2cc609 Rich Lane
    switchClass = UserSwitch
253 e1205a8a Brandon Heller
254
if __name__ == '__main__':
255
    setLogLevel( 'warning' )
256
    unittest.main()