Statistics
| Branch: | Tag: | Revision:

mininet / bin / mn @ 5ac3cde2

History | View | Annotate | Download (10.6 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, DefaultController, RemoteController, UserSwitch, OVSKernelSwitch,
29
                           OVSLegacyKernelSwitch, IVSSwitch )
30
from mininet.link import Link, TCLink
31
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
32
from mininet.topolib import TreeTopo
33
from mininet.util import custom, customConstructor
34
from mininet.util import buildTopo
35

    
36

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

    
45
SWITCHDEF = 'ovsk'
46
SWITCHES = { 'user': UserSwitch,
47
             'ovsk': OVSKernelSwitch,
48
             'ovsl': OVSLegacyKernelSwitch,
49
             'ivs': IVSSwitch }
50

    
51
HOSTDEF = 'proc'
52
HOSTS = { 'proc': Host,
53
          'rt': custom( CPULimitedHost, sched='rt' ),
54
          'cfs': custom( CPULimitedHost, sched='cfs' ) }
55

    
56
CONTROLLERDEF = 'default'
57
CONTROLLERS = { 'ref': Controller,
58
                'ovsc': OVSController,
59
                'nox': NOX,
60
                'remote': RemoteController,
61
                'default': DefaultController,
62
                'none': lambda name: None }
63

    
64
LINKDEF = 'default'
65
LINKS = { 'default': Link,
66
          'tc': TCLink }
67

    
68

    
69
# optional tests to run
70
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
71
          'none' ]
72

    
73
ALTSPELLING = { 'pingall': 'pingAll',
74
                'pingpair': 'pingPair',
75
                'iperfudp': 'iperfUdp',
76
                'iperfUDP': 'iperfUdp' }
77

    
78

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

    
97

    
98
def version( *_args ):
99
    "Print Mininet version and exit"
100
    print "%s" % VERSION
101
    exit()
102

    
103
class MininetRunner( object ):
104
    "Build, setup, and run Mininet."
105

    
106
    def __init__( self ):
107
        "Init."
108
        self.options = None
109
        self.args = None  # May be used someday for more CLI scripts
110
        self.validate = None
111

    
112
        self.parseArgs()
113
        self.setup()
114
        self.begin()
115

    
116
    def setCustom( self, name, value ):
117
        "Set custom parameters for MininetRunner."
118
        if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
119
            # Update dictionaries
120
            param = name.upper()
121
            globals()[ param ].update( value )
122
        elif name == 'validate':
123
            # Add custom validate function
124
            self.validate = value
125
        else:
126
            # Add or modify global variable or class
127
            globals()[ name ] = value
128

    
129
    def parseCustomFile( self, fileName ):
130
        "Parse custom file and add params before parsing cmd-line options."
131
        customs = {}
132
        if os.path.isfile( fileName ):
133
            execfile( fileName, customs, customs )
134
            for name, val in customs.iteritems():
135
                self.setCustom( name, val )
136
        else:
137
            raise Exception( 'could not find custom file: %s' % fileName )
138

    
139
    def parseArgs( self ):
140
        """Parse command-line args and return options object.
141
           returns: opts parse options dict"""
142
        if '--custom' in sys.argv:
143
            index = sys.argv.index( '--custom' )
144
            if len( sys.argv ) > index + 1:
145
                filename = sys.argv[ index + 1 ]
146
                self.parseCustomFile( filename )
147
            else:
148
                raise Exception( 'Custom file name not found' )
149

    
150
        desc = ( "The %prog utility creates Mininet network from the\n"
151
                 "command line. It can create parametrized topologies,\n"
152
                 "invoke the Mininet CLI, and run tests." )
153

    
154
        usage = ( '%prog [options]\n'
155
                  '(type %prog -h for details)' )
156

    
157
        opts = OptionParser( description=desc, usage=usage )
158
        addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
159
        addDictOption( opts, HOSTS, HOSTDEF, 'host' )
160
        addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
161
        addDictOption( opts, LINKS, LINKDEF, 'link' )
162
        addDictOption( opts, TOPOS, TOPODEF, 'topo' )
163

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

    
199
        self.options, self.args = opts.parse_args()
200

    
201
        # We don't accept extra arguments after the options
202
        if self.args:
203
            opts.print_help()
204
            exit()
205

    
206
    def setup( self ):
207
        "Setup and validate environment."
208

    
209
        # set logging verbosity
210
        if LEVELS[self.options.verbosity] > LEVELS['output']:
211
            print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
212
                    'output!\n'
213
                    'Please restart Mininet with -v [debug, info, output].'
214
                    % self.options.verbosity )
215
        lg.setLogLevel( self.options.verbosity )
216

    
217
    def begin( self ):
218
        "Create and run mininet."
219

    
220
        if self.options.clean:
221
            cleanup()
222
            exit()
223

    
224
        start = time.time()
225

    
226
        topo = buildTopo( TOPOS, self.options.topo )
227
        switch = customConstructor( SWITCHES, self.options.switch )
228
        host = customConstructor( HOSTS, self.options.host )
229
        controller = customConstructor( CONTROLLERS, self.options.controller )
230
        link = customConstructor( LINKS, self.options.link )
231

    
232
        if self.validate:
233
            self.validate( self.options )
234

    
235
        inNamespace = self.options.innamespace
236
        Net = MininetWithControlNet if inNamespace else Mininet
237
        ipBase = self.options.ipbase
238
        xterms = self.options.xterms
239
        mac = self.options.mac
240
        arp = self.options.arp
241
        pin = self.options.pin
242
        listenPort = None
243
        if not self.options.nolistenport:
244
            listenPort = self.options.listenport
245
        mn = Net( topo=topo,
246
                  switch=switch, host=host, controller=controller,
247
                  link=link,
248
                  ipBase=ipBase,
249
                  inNamespace=inNamespace,
250
                  xterms=xterms, autoSetMacs=mac,
251
                  autoStaticArp=arp, autoPinCpus=pin,
252
                  listenPort=listenPort )
253

    
254
        if self.options.pre:
255
            CLI( mn, script=self.options.pre )
256

    
257
        test = self.options.test
258
        test = ALTSPELLING.get( test, test )
259

    
260
        mn.start()
261

    
262
        if test == 'none':
263
            pass
264
        elif test == 'all':
265
            mn.start()
266
            mn.ping()
267
            mn.iperf()
268
        elif test == 'cli':
269
            CLI( mn )
270
        elif test != 'build':
271
            getattr( mn, test )()
272

    
273
        if self.options.post:
274
            CLI( mn, script=self.options.post )
275

    
276
        mn.stop()
277

    
278
        elapsed = float( time.time() - start )
279
        info( 'completed in %0.3f seconds\n' % elapsed )
280

    
281

    
282
if __name__ == "__main__":
283
    try:
284
        MininetRunner()
285
    except KeyboardInterrupt:
286
        info( "\n\nKeyboard Interrupt. Shutting down and cleaning up...\n\n")
287
        cleanup()
288
    except Exception:
289
        # Print exception
290
        type_, val_, trace_ = sys.exc_info()
291
        errorMsg = ( "-"*80 + "\n" +
292
                     "Caught exception. Cleaning up...\n\n" +
293
                     "%s: %s\n" % ( type_.__name__, val_ ) +
294
                     "-"*80 + "\n" )
295
        error( errorMsg )
296
        # Print stack trace to debug log
297
        import traceback
298
        stackTrace = traceback.format_exc()
299
        debug( stackTrace + "\n" )
300
        cleanup()