Statistics
| Branch: | Tag: | Revision:

mininet / bin / mn @ af1ccf93

History | View | Annotate | Download (14.8 KB)

1 a6b47322 Brandon Heller
#!/usr/bin/env python
2
3 8895862a Bob Lantz
"""
4
Mininet runner
5
author: Brandon Heller (brandonh@stanford.edu)
6 befa1310 Brandon Heller
7
To see options:
8
  sudo mn -h
9
10
Example to pull custom params (topo, switch, etc.) from a file:
11
  sudo mn --custom ~/mininet/custom/custom_example.py
12 8895862a Bob Lantz
"""
13 a6b47322 Brandon Heller
14
from optparse import OptionParser
15 e2b799b8 Bob Lantz
import os
16 befa1310 Brandon Heller
import sys
17 a6b47322 Brandon Heller
import time
18
19 e2b799b8 Bob Lantz
# Fix setuptools' evil madness, and open up (more?) security holes
20
if 'PYTHONPATH' in os.environ:
21
    sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
22
23 3eb5abe6 Brandon Heller
from mininet.clean import cleanup
24 d869d820 Bob Lantz
from mininet.cli import CLI
25 c265deed Bob Lantz
from mininet.log import lg, LEVELS, info, debug, warn, error
26 39128f8c Bob Lantz
from mininet.net import Mininet, MininetWithControlNet, VERSION
27 612b21cb Bob Lantz
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
28 1b69ea13 Bob Lantz
                           RYU, NOX, RemoteController, findController, DefaultController,
29
                           UserSwitch, OVSSwitch, OVSBridge,
30 27da832d Rich Lane
                           OVSLegacyKernelSwitch, IVSSwitch )
31 38addf2e Bob Lantz
from mininet.nodelib import LinuxBridge
32 ff568819 Bob Lantz
from mininet.link import Link, TCLink
33 6d2cd77b Brandon Heller
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
34 b5962e8e Bob Lantz
from mininet.topolib import TreeTopo, TorusTopo
35 af1ccf93 Brian O'Connor
from mininet.util import customConstructor, splitArgs
36 928c0761 Brandon Heller
from mininet.util import buildTopo
37 a6b47322 Brandon Heller
38 c265deed Bob Lantz
from functools import partial
39
40
# Experimental! cluster edition prototype
41
from mininet.examples.cluster import ( MininetCluster, RemoteHost,
42
                                       RemoteOVSSwitch, RemoteLink,
43
                                       SwitchBinPlacer, RandomPlacer )
44 dd876e69 Bob Lantz
from mininet.examples.clustercli import ClusterCLI
45 c265deed Bob Lantz
46
PLACEMENT = { 'block': SwitchBinPlacer, 'random': RandomPlacer }
47 2db4268b Bob Lantz
48 a6b47322 Brandon Heller
# built in topologies, created only when run
49 8895862a Bob Lantz
TOPODEF = 'minimal'
50 bb941950 Brandon Heller
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
51 edf60032 Brandon Heller
          'linear': LinearTopo,
52
          'reversed': SingleSwitchReversedTopo,
53
          'single': SingleSwitchTopo,
54 b5962e8e Bob Lantz
          'tree': TreeTopo,
55
          'torus': TorusTopo }
56 8895862a Bob Lantz
57 1b69ea13 Bob Lantz
SWITCHDEF = 'default'
58 7d557fd7 Bob Lantz
SWITCHES = { 'user': UserSwitch,
59 38addf2e Bob Lantz
             'ovs':  OVSSwitch,
60 1b69ea13 Bob Lantz
             'ovsbr' : OVSBridge,
61 38addf2e Bob Lantz
             # Keep ovsk for compatibility with 2.0
62
             'ovsk': OVSSwitch,
63 27da832d Rich Lane
             'ovsl': OVSLegacyKernelSwitch,
64 38addf2e Bob Lantz
             'ivs': IVSSwitch,
65 1b69ea13 Bob Lantz
             'lxbr': LinuxBridge,
66
             'default': OVSSwitch }
67 a6b47322 Brandon Heller
68 216a4b7c Bob Lantz
HOSTDEF = 'proc'
69
HOSTS = { 'proc': Host,
70 b739cd11 Bob Lantz
          'rt': partial( CPULimitedHost, sched='rt' ),
71
          'cfs': partial( CPULimitedHost, sched='cfs' ) }
72 a6b47322 Brandon Heller
73 72fd120d Cody Burkard
CONTROLLERDEF = 'default'
74 8895862a Bob Lantz
CONTROLLERS = { 'ref': Controller,
75 9addfc13 Bob Lantz
                'ovsc': OVSController,
76 14ff3ad3 Bob Lantz
                'nox': NOX,
77 9addfc13 Bob Lantz
                'remote': RemoteController,
78 686a9993 cody burkard
                'ryu': RYU,
79 1b69ea13 Bob Lantz
                'default': DefaultController,  # Note: replaced below
80 9addfc13 Bob Lantz
                'none': lambda name: None }
81 a6b47322 Brandon Heller
82 ff568819 Bob Lantz
LINKDEF = 'default'
83
LINKS = { 'default': Link,
84
          'tc': TCLink }
85 216a4b7c Bob Lantz
86
87 a6b47322 Brandon Heller
# optional tests to run
88 d869d820 Bob Lantz
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
89 edf60032 Brandon Heller
          'none' ]
90 8895862a Bob Lantz
91 edf60032 Brandon Heller
ALTSPELLING = { 'pingall': 'pingAll',
92
                'pingpair': 'pingPair',
93
                'iperfudp': 'iperfUdp',
94 8838c30e Bob Lantz
                'iperfUDP': 'iperfUdp' }
95 8895862a Bob Lantz
96 216a4b7c Bob Lantz
97 8895862a Bob Lantz
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
98
    """Convenience function to add choices dicts to OptionParser.
99
       opts: OptionParser instance
100
       choicesDict: dictionary of valid choices, must include default
101
       default: default choice key
102
       name: long option name
103
       help: string"""
104
    if default not in choicesDict:
105
        raise Exception( 'Invalid  default %s for choices dict: %s' %
106 edf60032 Brandon Heller
                         ( default, name ) )
107 8895862a Bob Lantz
    if not helpStr:
108 14ff3ad3 Bob Lantz
        helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
109 edf60032 Brandon Heller
                    '[,param=value...]' )
110 8895862a Bob Lantz
    opts.add_option( '--' + name,
111 edf60032 Brandon Heller
                     type='string',
112
                     default = default,
113
                     help = helpStr )
114 a6b47322 Brandon Heller
115
116 39128f8c Bob Lantz
def version( *_args ):
117
    "Print Mininet version and exit"
118 d85a58fe Bob Lantz
    print "%s" % VERSION
119 39128f8c Bob Lantz
    exit()
120
121 3ac0dd70 Brian O'Connor
122 8895862a Bob Lantz
class MininetRunner( object ):
123
    "Build, setup, and run Mininet."
124 a6b47322 Brandon Heller
125 8895862a Bob Lantz
    def __init__( self ):
126
        "Init."
127 a6b47322 Brandon Heller
        self.options = None
128 d869d820 Bob Lantz
        self.args = None  # May be used someday for more CLI scripts
129 befa1310 Brandon Heller
        self.validate = None
130 723d068c Brandon Heller
131 8895862a Bob Lantz
        self.parseArgs()
132 a6b47322 Brandon Heller
        self.setup()
133
        self.begin()
134 e0cfcdd5 Bob Lantz
135 b739cd11 Bob Lantz
    def custom( self, option, opt_str, value, parser ):
136
        """Parse custom file and add params.
137
           option: option e.g. --custom
138
           opt_str: option string e.g. --custom
139
           value: the value the follows the option
140
           parser: option parser instance"""
141
        files = []
142
        if os.path.isfile( value ):
143
            # Accept any single file (including those with commas)
144
            files.append( value )
145
        else:
146
            # Accept a comma-separated list of filenames
147
            files += value.split(',')
148
        
149
        for fileName in files:
150
            customs = {}
151
            if os.path.isfile( fileName ):
152
                execfile( fileName, customs, customs )
153
                for name, val in customs.iteritems():
154
                    self.setCustom( name, val )
155
            else:
156
                raise Exception( 'could not find custom file: %s' % fileName )
157
158 c3a44400 Bob Lantz
    def setCustom( self, name, value ):
159 e0cfcdd5 Bob Lantz
        "Set custom parameters for MininetRunner."
160 c3a44400 Bob Lantz
        if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
161
            # Update dictionaries
162
            param = name.upper()
163
            globals()[ param ].update( value )
164
        elif name == 'validate':
165
            # Add custom validate function
166
            self.validate = value
167
        else:
168
            # Add or modify global variable or class
169
            globals()[ name ] = value
170 e0cfcdd5 Bob Lantz
171 af1ccf93 Brian O'Connor
    def setNat( self, option, opt_str, value, parser ):
172
        parser.values.nat = True
173
        if parser.rargs and parser.rargs[ 0 ][ 0 ] != '-': #first arg, first char != '-'
174
            value = parser.rargs.pop( 0 )
175
            _, args, kwargs = splitArgs( opt_str + ',' + value )
176
            parser.values.nat_args = args
177
            parser.values.nat_kwargs = kwargs
178
        else:
179
            parser.values.nat_args = []
180
            parser.values.nat_kwargs = {}
181
182 8895862a Bob Lantz
    def parseArgs( self ):
183
        """Parse command-line args and return options object.
184
           returns: opts parse options dict"""
185 befa1310 Brandon Heller
186 f2e7884a Bob Lantz
        desc = ( "The %prog utility creates Mininet network from the\n"
187
                 "command line. It can create parametrized topologies,\n"
188
                 "invoke the Mininet CLI, and run tests." )
189
190 01e0758e Bob Lantz
        usage = ( '%prog [options]\n'
191
                  '(type %prog -h for details)' )
192
193
        opts = OptionParser( description=desc, usage=usage )
194 8895862a Bob Lantz
        addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
195
        addDictOption( opts, HOSTS, HOSTDEF, 'host' )
196
        addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
197 ff568819 Bob Lantz
        addDictOption( opts, LINKS, LINKDEF, 'link' )
198 216a4b7c Bob Lantz
        addDictOption( opts, TOPOS, TOPODEF, 'topo' )
199 8895862a Bob Lantz
200 3eb5abe6 Brandon Heller
        opts.add_option( '--clean', '-c', action='store_true',
201 edf60032 Brandon Heller
                         default=False, help='clean and exit' )
202 b739cd11 Bob Lantz
        opts.add_option( '--custom', action='callback',
203 593fc365 Brian O'Connor
                         callback=self.custom,
204 b739cd11 Bob Lantz
                         type='string', help='read custom classes or params from .py file(s)' )
205 8895862a Bob Lantz
        opts.add_option( '--test', type='choice', choices=TESTS,
206 edf60032 Brandon Heller
                         default=TESTS[ 0 ],
207
                         help='|'.join( TESTS ) )
208 8895862a Bob Lantz
        opts.add_option( '--xterms', '-x', action='store_true',
209 edf60032 Brandon Heller
                         default=False, help='spawn xterms for each node' )
210 82f483f5 Bob Lantz
        opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
211
                         help='base IP address for hosts' )
212 8895862a Bob Lantz
        opts.add_option( '--mac', action='store_true',
213 edf60032 Brandon Heller
                         default=False, help='automatically set host MACs' )
214 8895862a Bob Lantz
        opts.add_option( '--arp', action='store_true',
215 edf60032 Brandon Heller
                         default=False, help='set all-pairs ARP entries' )
216 8895862a Bob Lantz
        opts.add_option( '--verbosity', '-v', type='choice',
217 edf60032 Brandon Heller
                         choices=LEVELS.keys(), default = 'info',
218
                         help = '|'.join( LEVELS.keys() )  )
219 d869d820 Bob Lantz
        opts.add_option( '--innamespace', action='store_true',
220 edf60032 Brandon Heller
                         default=False, help='sw and ctrl in namespace?' )
221 bd964adb Brandon Heller
        opts.add_option( '--listenport', type='int', default=6634,
222 216a4b7c Bob Lantz
                         help='base port for passive switch listening' )
223 0a9358c9 Brandon Heller
        opts.add_option( '--nolistenport', action='store_true',
224 9330a33f Brandon Heller
                         default=False, help="don't use passive listening " +
225
                         "port")
226 d869d820 Bob Lantz
        opts.add_option( '--pre', type='string', default=None,
227 edf60032 Brandon Heller
                         help='CLI script to run before tests' )
228 d869d820 Bob Lantz
        opts.add_option( '--post', type='string', default=None,
229 edf60032 Brandon Heller
                         help='CLI script to run after tests' )
230 197b083f Bob Lantz
        opts.add_option( '--pin', action='store_true',
231
                         default=False, help="pin hosts to CPU cores "
232
                         "(requires --host cfs or --host rt)" )
233 af1ccf93 Brian O'Connor
        opts.add_option( '--nat', action='callback', callback=self.setNat,
234
                         help="adds a NAT to the topology that connects Mininet hosts"
235
                         " to the physical network."
236
                         " Warning: This may route any traffic on the machine that uses Mininet's"
237
                         " IP subnet into the Mininet network. If you need to change"
238
                         " Mininet's IP subnet, see the --ipbase option." )
239 3ac0dd70 Brian O'Connor
        opts.add_option( '--version', action='callback', callback=version,
240
                         help='prints the version and exits' )
241 c265deed Bob Lantz
        opts.add_option( '--cluster', type='string', default=None,
242
                         metavar='server1,server2...',
243
                         help=( 'run on multiple servers (experimental!)' ) )
244
        opts.add_option( '--placement', type='choice',
245
                         choices=PLACEMENT.keys(), default='block',
246
                         metavar='block|random',
247
                         help=( 'node placement for --cluster '
248
                                '(experimental!) ' ) )
249 87737a70 Bob Lantz
250 d869d820 Bob Lantz
        self.options, self.args = opts.parse_args()
251 8895862a Bob Lantz
252 294bbad4 Bob Lantz
        # We don't accept extra arguments after the options
253
        if self.args:
254
            opts.print_help()
255
            exit()
256
257 8895862a Bob Lantz
    def setup( self ):
258
        "Setup and validate environment."
259 a6b47322 Brandon Heller
260
        # set logging verbosity
261 cdeaca86 Brandon Heller
        if LEVELS[self.options.verbosity] > LEVELS['output']:
262 1a40cd04 Brandon Heller
            print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
263
                    'output!\n'
264 1dcc0476 Bob Lantz
                    'Please restart Mininet with -v [debug, info, output].'
265
                    % self.options.verbosity )
266 8895862a Bob Lantz
        lg.setLogLevel( self.options.verbosity )
267 a6b47322 Brandon Heller
268 8895862a Bob Lantz
    def begin( self ):
269
        "Create and run mininet."
270 a6b47322 Brandon Heller
271 3eb5abe6 Brandon Heller
        if self.options.clean:
272
            cleanup()
273
            exit()
274
275 a6b47322 Brandon Heller
        start = time.time()
276
277 1b69ea13 Bob Lantz
        if self.options.controller == 'default':
278
            # Update default based on available controllers
279
            CONTROLLERS[ 'default' ] = findController()
280
            if CONTROLLERS[ 'default' ] is None:
281
                if self.options.switch == 'default':
282
                    # Fall back to OVS Bridge, which does not use an OF controller
283
                    info( '*** No default OpenFlow controller found for default switch!\n' )
284
                    info( '*** Falling back to OVS Bridge\n' )
285
                    self.options.switch = 'ovsbr'
286
                    self.options.controller = 'none'
287
                elif self.options.switch in ( 'ovsbr', 'lxbr' ):
288
                    self.options.controller = 'none'
289
                else:
290
                    raise Exception( "Could not find a default controller for switch %s" %
291
                                     self.options.switch )
292
        
293 928c0761 Brandon Heller
        topo = buildTopo( TOPOS, self.options.topo )
294 30b4b4e7 Brandon Heller
        switch = customConstructor( SWITCHES, self.options.switch )
295
        host = customConstructor( HOSTS, self.options.host )
296
        controller = customConstructor( CONTROLLERS, self.options.controller )
297
        link = customConstructor( LINKS, self.options.link )
298 a6b47322 Brandon Heller
299 befa1310 Brandon Heller
        if self.validate:
300 c3a44400 Bob Lantz
            self.validate( self.options )
301 befa1310 Brandon Heller
302 82f483f5 Bob Lantz
        ipBase = self.options.ipbase
303 8a034f4f Brandon Heller
        xterms = self.options.xterms
304 376bcba4 Brandon Heller
        mac = self.options.mac
305
        arp = self.options.arp
306 197b083f Bob Lantz
        pin = self.options.pin
307 0a9358c9 Brandon Heller
        listenPort = None
308
        if not self.options.nolistenport:
309
            listenPort = self.options.listenport
310 c265deed Bob Lantz
311
        # Handle inNamespace, cluster options
312
        inNamespace = self.options.innamespace
313
        cluster = self.options.cluster
314
        if inNamespace and cluster:
315
            print "Please specify --innamespace OR --cluster"
316
            exit()
317
        Net = MininetWithControlNet if inNamespace else Mininet
318 43461905 Bob Lantz
        cli = ClusterCLI if cluster else CLI
319 c265deed Bob Lantz
        if cluster:
320
            warn( '*** WARNING: Experimental cluster mode!\n'
321
              '*** Using RemoteHost, RemoteOVSSwitch, RemoteLink\n' )
322
            host, switch, link = RemoteHost, RemoteOVSSwitch, RemoteLink
323
            Net = partial( MininetCluster, servers=cluster.split( ',' ),
324
                           placement=PLACEMENT[ self.options.placement ] )
325
326 14ff3ad3 Bob Lantz
        mn = Net( topo=topo,
327
                  switch=switch, host=host, controller=controller,
328 ff568819 Bob Lantz
                  link=link,
329 14ff3ad3 Bob Lantz
                  ipBase=ipBase,
330
                  inNamespace=inNamespace,
331
                  xterms=xterms, autoSetMacs=mac,
332 197b083f Bob Lantz
                  autoStaticArp=arp, autoPinCpus=pin,
333
                  listenPort=listenPort )
334 87737a70 Bob Lantz
335 ffeb16eb Brian O'Connor
        if self.options.nat:
336 af1ccf93 Brian O'Connor
            nat = mn.addNAT( *self.options.nat_args, **self.options.nat_kwargs )
337 e0af1602 Brian O'Connor
            nat.configDefault()
338 ffeb16eb Brian O'Connor
339 d869d820 Bob Lantz
        if self.options.pre:
340 43461905 Bob Lantz
            cli( mn, script=self.options.pre )
341 a6b47322 Brandon Heller
342 eeb9cb3c Brandon Heller
        test = self.options.test
343 d869d820 Bob Lantz
        test = ALTSPELLING.get( test, test )
344 f95aebb4 Bob Lantz
345 d869d820 Bob Lantz
        mn.start()
346 f95aebb4 Bob Lantz
347 dfc08a86 Brandon Heller
        if test == 'none':
348 d869d820 Bob Lantz
            pass
349 dfc08a86 Brandon Heller
        elif test == 'all':
350 84ea8d7f Cody Burkard
            mn.waitConnected()
351 dfc08a86 Brandon Heller
            mn.start()
352
            mn.ping()
353
            mn.iperf()
354 d869d820 Bob Lantz
        elif test == 'cli':
355 c4fc6304 Bob Lantz
            cli( mn )
356 dfc08a86 Brandon Heller
        elif test != 'build':
357 84ea8d7f Cody Burkard
            mn.waitConnected()
358 d869d820 Bob Lantz
            getattr( mn, test )()
359
360
        if self.options.post:
361 c4fc6304 Bob Lantz
            cli( mn, script=self.options.post )
362 d869d820 Bob Lantz
363
        mn.stop()
364 a6b47322 Brandon Heller
365 8895862a Bob Lantz
        elapsed = float( time.time() - start )
366 4e69ae83 Brandon Heller
        info( 'completed in %0.3f seconds\n' % elapsed )
367 a6b47322 Brandon Heller
368
369
if __name__ == "__main__":
370 bda2317d Brian O'Connor
    try:
371
        MininetRunner()
372
    except KeyboardInterrupt:
373 bb0006b6 Brian O'Connor
        info( "\n\nKeyboard Interrupt. Shutting down and cleaning up...\n\n")
374 bda2317d Brian O'Connor
        cleanup()
375
    except Exception:
376 19bc1df1 Brian O'Connor
        # Print exception
377 bda2317d Brian O'Connor
        type_, val_, trace_ = sys.exc_info()
378
        errorMsg = ( "-"*80 + "\n" +
379
                     "Caught exception. Cleaning up...\n\n" +
380
                     "%s: %s\n" % ( type_.__name__, val_ ) +
381
                     "-"*80 + "\n" )
382
        error( errorMsg )
383
        # Print stack trace to debug log
384
        import traceback
385
        stackTrace = traceback.format_exc()
386
        debug( stackTrace + "\n" )
387
        cleanup()