Statistics
| Branch: | Tag: | Revision:

mininet / bin / mn @ e8623fdc

History | View | Annotate | Download (11 KB)

1
#!/usr/bin/env python
2

    
3
"""
4
Mininet runner
5
author: Brandon Heller (brandonh@stanford.edu)
6

    
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
"""
13

    
14
from optparse import OptionParser
15
import os
16
import sys
17
import time
18

    
19
# 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
from mininet.clean import cleanup
24
from mininet.cli import CLI
25
from mininet.log import lg, LEVELS, info, debug, error
26
from mininet.net import Mininet, MininetWithControlNet, VERSION
27
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
28
                           NOX, RemoteController, DefaultController,
29
                           UserSwitch, OVSBridge, OVSSwitch,
30
                           OVSLegacyKernelSwitch, IVSSwitch )
31
from mininet.nodelib import LinuxBridge
32
from mininet.link import Link, TCLink
33
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
34
from mininet.topolib import TreeTopo, TorusTopo
35
from mininet.util import custom, customConstructor
36
from mininet.util import buildTopo
37

    
38

    
39
# built in topologies, created only when run
40
TOPODEF = 'minimal'
41
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
42
          'linear': LinearTopo,
43
          'reversed': SingleSwitchReversedTopo,
44
          'single': SingleSwitchTopo,
45
          'tree': TreeTopo,
46
          'torus': TorusTopo }
47

    
48
SWITCHDEF = 'ovsk'
49
SWITCHES = { 'user': UserSwitch,
50
             'ovs':  OVSSwitch,
51
             'ovsb' : OVSBridge,
52
             # Keep ovsk for compatibility with 2.0
53
             'ovsk': OVSSwitch,
54
             'ovsl': OVSLegacyKernelSwitch,
55
             'ivs': IVSSwitch,
56
             'lxbr': LinuxBridge }
57

    
58
HOSTDEF = 'proc'
59
HOSTS = { 'proc': Host,
60
          'rt': custom( CPULimitedHost, sched='rt' ),
61
          'cfs': custom( CPULimitedHost, sched='cfs' ) }
62

    
63
CONTROLLERDEF = 'default'
64
CONTROLLERS = { 'ref': Controller,
65
                'ovsc': OVSController,
66
                'nox': NOX,
67
                'remote': RemoteController,
68
                'default': DefaultController,
69
                'none': lambda name: None }
70

    
71
LINKDEF = 'default'
72
LINKS = { 'default': Link,
73
          'tc': TCLink }
74

    
75

    
76
# optional tests to run
77
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
78
          'none' ]
79

    
80
ALTSPELLING = { 'pingall': 'pingAll',
81
                'pingpair': 'pingPair',
82
                'iperfudp': 'iperfUdp',
83
                'iperfUDP': 'iperfUdp' }
84

    
85

    
86
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
87
    """Convenience function to add choices dicts to OptionParser.
88
       opts: OptionParser instance
89
       choicesDict: dictionary of valid choices, must include default
90
       default: default choice key
91
       name: long option name
92
       help: string"""
93
    if default not in choicesDict:
94
        raise Exception( 'Invalid  default %s for choices dict: %s' %
95
                         ( default, name ) )
96
    if not helpStr:
97
        helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
98
                    '[,param=value...]' )
99
    opts.add_option( '--' + name,
100
                     type='string',
101
                     default = default,
102
                     help = helpStr )
103

    
104

    
105
def version( *_args ):
106
    "Print Mininet version and exit"
107
    print "%s" % VERSION
108
    exit()
109

    
110
class MininetRunner( object ):
111
    "Build, setup, and run Mininet."
112

    
113
    def __init__( self ):
114
        "Init."
115
        self.options = None
116
        self.args = None  # May be used someday for more CLI scripts
117
        self.validate = None
118

    
119
        self.parseArgs()
120
        self.setup()
121
        self.begin()
122

    
123
    def setCustom( self, name, value ):
124
        "Set custom parameters for MininetRunner."
125
        if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
126
            # Update dictionaries
127
            param = name.upper()
128
            globals()[ param ].update( value )
129
        elif name == 'validate':
130
            # Add custom validate function
131
            self.validate = value
132
        else:
133
            # Add or modify global variable or class
134
            globals()[ name ] = value
135

    
136
    def parseCustomFile( self, fileName ):
137
        "Parse custom file and add params before parsing cmd-line options."
138
        customs = {}
139
        if os.path.isfile( fileName ):
140
            execfile( fileName, customs, customs )
141
            for name, val in customs.iteritems():
142
                self.setCustom( name, val )
143
        else:
144
            raise Exception( 'could not find custom file: %s' % fileName )
145

    
146
    def parseArgs( self ):
147
        """Parse command-line args and return options object.
148
           returns: opts parse options dict"""
149
        if '--custom' in sys.argv:
150
            index = sys.argv.index( '--custom' )
151
            if len( sys.argv ) > index + 1:
152
                filename = sys.argv[ index + 1 ]
153
                self.parseCustomFile( filename )
154
            else:
155
                raise Exception( 'Custom file name not found' )
156

    
157
        desc = ( "The %prog utility creates Mininet network from the\n"
158
                 "command line. It can create parametrized topologies,\n"
159
                 "invoke the Mininet CLI, and run tests." )
160

    
161
        usage = ( '%prog [options]\n'
162
                  '(type %prog -h for details)' )
163

    
164
        opts = OptionParser( description=desc, usage=usage )
165
        addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
166
        addDictOption( opts, HOSTS, HOSTDEF, 'host' )
167
        addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
168
        addDictOption( opts, LINKS, LINKDEF, 'link' )
169
        addDictOption( opts, TOPOS, TOPODEF, 'topo' )
170

    
171
        opts.add_option( '--clean', '-c', action='store_true',
172
                         default=False, help='clean and exit' )
173
        opts.add_option( '--custom', type='string', default=None,
174
                         help='read custom topo and node params from .py' +
175
                         'file' )
176
        opts.add_option( '--test', type='choice', choices=TESTS,
177
                         default=TESTS[ 0 ],
178
                         help='|'.join( TESTS ) )
179
        opts.add_option( '--xterms', '-x', action='store_true',
180
                         default=False, help='spawn xterms for each node' )
181
        opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
182
                         help='base IP address for hosts' )
183
        opts.add_option( '--mac', action='store_true',
184
                         default=False, help='automatically set host MACs' )
185
        opts.add_option( '--arp', action='store_true',
186
                         default=False, help='set all-pairs ARP entries' )
187
        opts.add_option( '--verbosity', '-v', type='choice',
188
                         choices=LEVELS.keys(), default = 'info',
189
                         help = '|'.join( LEVELS.keys() )  )
190
        opts.add_option( '--innamespace', action='store_true',
191
                         default=False, help='sw and ctrl in namespace?' )
192
        opts.add_option( '--listenport', type='int', default=6634,
193
                         help='base port for passive switch listening' )
194
        opts.add_option( '--nolistenport', action='store_true',
195
                         default=False, help="don't use passive listening " +
196
                         "port")
197
        opts.add_option( '--pre', type='string', default=None,
198
                         help='CLI script to run before tests' )
199
        opts.add_option( '--post', type='string', default=None,
200
                         help='CLI script to run after tests' )
201
        opts.add_option( '--pin', action='store_true',
202
                         default=False, help="pin hosts to CPU cores "
203
                         "(requires --host cfs or --host rt)" )
204
        opts.add_option( '--version', action='callback', callback=version )
205

    
206
        self.options, self.args = opts.parse_args()
207

    
208
        # We don't accept extra arguments after the options
209
        if self.args:
210
            opts.print_help()
211
            exit()
212

    
213
    def setup( self ):
214
        "Setup and validate environment."
215

    
216
        # set logging verbosity
217
        if LEVELS[self.options.verbosity] > LEVELS['output']:
218
            print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
219
                    'output!\n'
220
                    'Please restart Mininet with -v [debug, info, output].'
221
                    % self.options.verbosity )
222
        lg.setLogLevel( self.options.verbosity )
223

    
224
    def begin( self ):
225
        "Create and run mininet."
226

    
227
        if self.options.clean:
228
            cleanup()
229
            exit()
230

    
231
        start = time.time()
232

    
233
        topo = buildTopo( TOPOS, self.options.topo )
234
        switch = customConstructor( SWITCHES, self.options.switch )
235
        host = customConstructor( HOSTS, self.options.host )
236
        controller = customConstructor( CONTROLLERS, self.options.controller )
237
        link = customConstructor( LINKS, self.options.link )
238

    
239
        if self.validate:
240
            self.validate( self.options )
241

    
242
        inNamespace = self.options.innamespace
243
        Net = MininetWithControlNet if inNamespace else Mininet
244
        ipBase = self.options.ipbase
245
        xterms = self.options.xterms
246
        mac = self.options.mac
247
        arp = self.options.arp
248
        pin = self.options.pin
249
        listenPort = None
250
        if not self.options.nolistenport:
251
            listenPort = self.options.listenport
252
        mn = Net( topo=topo,
253
                  switch=switch, host=host, controller=controller,
254
                  link=link,
255
                  ipBase=ipBase,
256
                  inNamespace=inNamespace,
257
                  xterms=xterms, autoSetMacs=mac,
258
                  autoStaticArp=arp, autoPinCpus=pin,
259
                  listenPort=listenPort )
260

    
261
        if self.options.pre:
262
            CLI( mn, script=self.options.pre )
263

    
264
        test = self.options.test
265
        test = ALTSPELLING.get( test, test )
266

    
267
        mn.start()
268

    
269
        if test == 'none':
270
            pass
271
        elif test == 'all':
272
            mn.waitConnected()
273
            mn.start()
274
            mn.ping()
275
            mn.iperf()
276
        elif test == 'cli':
277
            CLI( mn )
278
        elif test != 'build':
279
            mn.waitConnected()
280
            getattr( mn, test )()
281

    
282
        if self.options.post:
283
            CLI( mn, script=self.options.post )
284

    
285
        mn.stop()
286

    
287
        elapsed = float( time.time() - start )
288
        info( 'completed in %0.3f seconds\n' % elapsed )
289

    
290

    
291
if __name__ == "__main__":
292
    try:
293
        MininetRunner()
294
    except KeyboardInterrupt:
295
        info( "\n\nKeyboard Interrupt. Shutting down and cleaning up...\n\n")
296
        cleanup()
297
    except Exception:
298
        # Print exception
299
        type_, val_, trace_ = sys.exc_info()
300
        errorMsg = ( "-"*80 + "\n" +
301
                     "Caught exception. Cleaning up...\n\n" +
302
                     "%s: %s\n" % ( type_.__name__, val_ ) +
303
                     "-"*80 + "\n" )
304
        error( errorMsg )
305
        # Print stack trace to debug log
306
        import traceback
307
        stackTrace = traceback.format_exc()
308
        debug( stackTrace + "\n" )
309
        cleanup()