Statistics
| Branch: | Tag: | Revision:

mininet / bin / mn @ 197b083f

History | View | Annotate | Download (10.9 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.path
16
import sys
17
import time
18

    
19
from mininet.clean import cleanup
20
from mininet.cli import CLI
21
from mininet.log import lg, LEVELS, info, warn
22
from mininet.net import Mininet, MininetWithControlNet
23
from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
24
                           NOX, RemoteController, UserSwitch, OVSKernelSwitch,
25
                           OVSLegacyKernelSwitch )
26
from mininet.link import Link, TCLink
27
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
28
from mininet.topolib import TreeTopo
29
from mininet.util import makeNumeric, custom
30

    
31

    
32
def customNode( constructors, argStr ):
33
    "Return custom Node constructor based on argStr"
34
    cname, newargs, kwargs = splitArgs( argStr )
35
    constructor = constructors.get( cname, None )
36

    
37
    if not constructor:
38
        raise Exception( "error: %s is unknown - please specify one of %s" %
39
               ( cname, constructors.keys() ) )
40

    
41
    def customized( name, *args, **params ):
42
        "Customized Node constructor"
43
        params.update( kwargs )
44
        if not newargs:
45
            return constructor( name, *args, **params )
46
        if args:
47
            warn( 'warning: %s replacing %s with %s\n' % (
48
                  constructor, args, newargs ) )
49
        return constructor( name, *newargs, **params )
50

    
51
    return customized
52

    
53

    
54
# built in topologies, created only when run
55
TOPODEF = 'minimal'
56
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
57
         'linear': LinearTopo,
58
         'reversed': SingleSwitchReversedTopo,
59
         'single': SingleSwitchTopo,
60
         'tree': TreeTopo }
61

    
62
SWITCHDEF = 'ovsk'
63
SWITCHES = { 'user': UserSwitch,
64
            'ovsk': OVSKernelSwitch,
65
             'ovsl': OVSLegacyKernelSwitch }
66

    
67
HOSTDEF = 'proc'
68
HOSTS = { 'proc': Host,
69
          'rt': custom( CPULimitedHost, sched='rt' ),
70
          'cfs': custom( CPULimitedHost, sched='cfs' ) }
71

    
72
CONTROLLERDEF = 'ref'
73
CONTROLLERS = { 'ref': Controller,
74
                'ovsc': OVSController,
75
                'nox': NOX,
76
                'remote': RemoteController,
77
                'none': lambda name: None }
78

    
79
LINKDEF = 'default'
80
LINKS = { 'default': Link,
81
          'tc': TCLink }
82

    
83

    
84
# optional tests to run
85
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
86
         'none' ]
87

    
88
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
89
    'iperfudp': 'iperfUdp', 'iperfUDP': 'iperfUdp', 'prefixlen': 'prefixLen' }
90

    
91

    
92
def splitArgs( argstr ):
93
    """Split argument string into usable python arguments
94
       argstr: argument string with format fn,arg2,kw1=arg3...
95
       returns: fn, args, kwargs"""
96
    split = argstr.split( ',' )
97
    fn = split[ 0 ]
98
    params = split[ 1: ]
99
    # Convert int and float args; removes the need for function
100
    # to be flexible with input arg formats.
101
    args = [ makeNumeric( s ) for s in params if '=' not in s ]
102
    kwargs = {}
103
    for s in [ p for p in params if '=' in p ]:
104
        key, val = s.split( '=' )
105
        kwargs[ key ] = makeNumeric( val )
106
    return fn, args, kwargs
107

    
108

    
109
def buildTopo( topoStr ):
110
    "Create topology from string with format (object, arg1, arg2,...)."
111
    topo, args, kwargs = splitArgs( topoStr )
112
    if topo not in TOPOS:
113
        raise Exception( 'Invalid topo name %s' % topo )
114
    return TOPOS[ topo ]( *args, **kwargs )
115

    
116

    
117
def addDictOption( opts, choicesDict, default, name, helpStr=None ):
118
    """Convenience function to add choices dicts to OptionParser.
119
       opts: OptionParser instance
120
       choicesDict: dictionary of valid choices, must include default
121
       default: default choice key
122
       name: long option name
123
       help: string"""
124
    if default not in choicesDict:
125
        raise Exception( 'Invalid  default %s for choices dict: %s' %
126
                        ( default, name ) )
127
    if not helpStr:
128
        helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
129
                   '[,param=value...]' )
130
    opts.add_option( '--' + name,
131
                    type='string',
132
                    default = default,
133
                    help = helpStr )
134

    
135

    
136
class MininetRunner( object ):
137
    "Build, setup, and run Mininet."
138

    
139
    def __init__( self ):
140
        "Init."
141
        self.options = None
142
        self.args = None  # May be used someday for more CLI scripts
143
        self.validate = None
144

    
145
        self.parseArgs()
146
        self.setup()
147
        self.begin()
148

    
149
    def setCustom( self, name, value ):
150
        "Set custom parameters for MininetRunner."
151
        if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
152
            # Update dictionaries
153
            param = name.upper()
154
            globals()[ param ].update( value )
155
        elif name == 'validate':
156
            # Add custom validate function
157
            self.validate = value
158
        else:
159
            # Add or modify global variable or class
160
            globals()[ name ] = value
161

    
162
    def parseCustomFile( self, fileName ):
163
        "Parse custom file and add params before parsing cmd-line options."
164
        customs = {}
165
        if os.path.isfile( fileName ):
166
            execfile( fileName, customs, customs )
167
            for name, val in customs.iteritems():
168
                self.setCustom( name, val )
169
        else:
170
            raise Exception( 'could not find custom file: %s' % fileName )
171

    
172
    def parseArgs( self ):
173
        """Parse command-line args and return options object.
174
           returns: opts parse options dict"""
175
        if '--custom' in sys.argv:
176
            index = sys.argv.index( '--custom' )
177
            if len( sys.argv ) > index + 1:
178
                filename = sys.argv[ index + 1 ]
179
                self.parseCustomFile( filename )
180
            else:
181
                raise Exception( 'Custom file name not found' )
182

    
183
        opts = OptionParser()
184
        addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
185
        addDictOption( opts, HOSTS, HOSTDEF, 'host' )
186
        addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
187
        addDictOption( opts, LINKS, LINKDEF, 'link' )
188
        addDictOption( opts, TOPOS, TOPODEF, 'topo' )
189

    
190
        opts.add_option( '--clean', '-c', action='store_true',
191
                        default=False, help='clean and exit' )
192
        opts.add_option( '--custom', type='string', default=None,
193
                        help='read custom topo and node params from .py file' )
194
        opts.add_option( '--test', type='choice', choices=TESTS,
195
                        default=TESTS[ 0 ],
196
                        help='|'.join( TESTS ) )
197
        opts.add_option( '--xterms', '-x', action='store_true',
198
                        default=False, help='spawn xterms for each node' )
199
        opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
200
                         help='base IP address for hosts' )
201
        opts.add_option( '--mac', action='store_true',
202
                        default=False, help='automatically set host MACs' )
203
        opts.add_option( '--arp', action='store_true',
204
                        default=False, help='set all-pairs ARP entries' )
205
        opts.add_option( '--verbosity', '-v', type='choice',
206
                        choices=LEVELS.keys(), default = 'info',
207
                        help = '|'.join( LEVELS.keys() )  )
208
        opts.add_option( '--ip', type='string', default='127.0.0.1',
209
                        help='ip address as a dotted decimal string for a'
210
                        'remote controller' )
211
        opts.add_option( '--innamespace', action='store_true',
212
                        default=False, help='sw and ctrl in namespace?' )
213
        opts.add_option( '--listenport', type='int', default=6635,
214
                         help='base port for passive switch listening' )
215
        opts.add_option( '--nolistenport', action='store_true',
216
                        default=False, help="don't use passive listening port")
217
        opts.add_option( '--pre', type='string', default=None,
218
                        help='CLI script to run before tests' )
219
        opts.add_option( '--post', type='string', default=None,
220
                        help='CLI script to run after tests' )
221
        opts.add_option( '--prefixlen', type='int', default=8,
222
                        help='prefix length (e.g. /8) for automatic '
223
                        'network configuration' )
224
        opts.add_option( '--pin', action='store_true',
225
                         default=False, help="pin hosts to CPU cores "
226
                         "(requires --host cfs or --host rt)" )
227

    
228
        self.options, self.args = opts.parse_args()
229

    
230
    def setup( self ):
231
        "Setup and validate environment."
232

    
233
        # set logging verbosity
234
        if LEVELS[self.options.verbosity] > LEVELS['output']:
235
            print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
236
                    'output!\n'
237
                    'Please restart Mininet with -v [debug, info, output].'
238
                    % self.options.verbosity )
239
        lg.setLogLevel( self.options.verbosity )
240

    
241
    def begin( self ):
242
        "Create and run mininet."
243

    
244
        if self.options.clean:
245
            cleanup()
246
            exit()
247

    
248
        start = time.time()
249

    
250
        topo = buildTopo( self.options.topo )
251
        switch = customNode( SWITCHES, self.options.switch )
252
        host = customNode( HOSTS, self.options.host )
253
        controller = customNode( CONTROLLERS, self.options.controller )
254
        link = customNode( LINKS, self.options.link )
255

    
256
        if self.validate:
257
            self.validate( self.options )
258

    
259
        inNamespace = self.options.innamespace
260
        Net = MininetWithControlNet if inNamespace else Mininet
261
        ipBase = self.options.ipbase
262
        xterms = self.options.xterms
263
        mac = self.options.mac
264
        arp = self.options.arp
265
        pin = self.options.pin
266
        listenPort = None
267
        if not self.options.nolistenport:
268
            listenPort = self.options.listenport
269
        mn = Net( topo=topo,
270
                  switch=switch, host=host, controller=controller,
271
                  link=link,
272
                  ipBase=ipBase,
273
                  inNamespace=inNamespace,
274
                  xterms=xterms, autoSetMacs=mac,
275
                  autoStaticArp=arp, autoPinCpus=pin,
276
                  listenPort=listenPort )
277

    
278
        if self.options.pre:
279
            CLI( mn, script=self.options.pre )
280

    
281
        test = self.options.test
282
        test = ALTSPELLING.get( test, test )
283

    
284
        mn.start()
285

    
286
        if test == 'none':
287
            pass
288
        elif test == 'all':
289
            mn.start()
290
            mn.ping()
291
            mn.iperf()
292
        elif test == 'cli':
293
            CLI( mn )
294
        elif test != 'build':
295
            getattr( mn, test )()
296

    
297
        if self.options.post:
298
            CLI( mn, script=self.options.post )
299

    
300
        mn.stop()
301

    
302
        elapsed = float( time.time() - start )
303
        info( 'completed in %0.3f seconds\n' % elapsed )
304

    
305

    
306
if __name__ == "__main__":
307
    MininetRunner()