Statistics
| Branch: | Tag: | Revision:

mininet / bin / mn @ 87737a70

History | View | Annotate | Download (10 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
22
from mininet.net import Mininet, init
23
from mininet.node import KernelSwitch, Host, Controller, ControllerParams, NOX
24
from mininet.node import RemoteController, UserSwitch, OVSKernelSwitch
25
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
26
from mininet.topolib import TreeTopo
27
from mininet.util import makeNumeric
28

    
29
# built in topologies, created only when run
30
TOPODEF = 'minimal'
31
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
32
         'linear': LinearTopo,
33
         'reversed': SingleSwitchReversedTopo,
34
         'single': SingleSwitchTopo,
35
         'tree': TreeTopo }
36

    
37
SWITCHDEF = 'ovsk'
38
SWITCHES = { 'kernel': KernelSwitch,
39
            'user': UserSwitch,
40
            'ovsk': OVSKernelSwitch }
41

    
42
HOSTDEF = 'process'
43
HOSTS = { 'process': Host }
44

    
45
CONTROLLERDEF = 'ref'
46
# a and b are the name and inNamespace params.
47
CONTROLLERS = { 'ref': Controller,
48
               'nox_dump': lambda name: NOX( name, 'packetdump' ),
49
               'nox_pysw': lambda name: NOX( name, 'pyswitch' ),
50
               'remote': lambda name: None,
51
               'none': lambda name: None }
52

    
53
# optional tests to run
54
TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
55
         'none' ]
56

    
57
ALTSPELLING = { 'pingall': 'pingAll', 'pingpair': 'pingPair',
58
    'iperfudp': 'iperfUdp', 'iperfUDP': 'iperfUdp', 'prefixlen': 'prefixLen' }
59

    
60
def buildTopo( topo ):
61
    "Create topology from string with format (object, arg1, arg2,...)."
62
    topo_split = topo.split( ',' )
63
    topo_name = topo_split[ 0 ]
64
    topo_params = topo_split[ 1: ]
65

    
66
    # Convert int and float args; removes the need for every topology to
67
    # be flexible with input arg formats.
68
    topo_seq_params = [ s for s in topo_params if '=' not in s ]
69
    topo_seq_params = [ makeNumeric( s ) for s in topo_seq_params ]
70
    topo_kw_params = {}
71
    for s in [ p for p in topo_params if '=' in p ]:
72
        key, val = s.split( '=' )
73
        topo_kw_params[ key ] = makeNumeric( val )
74

    
75
    if topo_name not in TOPOS.keys():
76
        raise Exception( 'Invalid topo_name %s' % topo_name )
77
    return TOPOS[ topo_name ]( *topo_seq_params, **topo_kw_params )
78

    
79

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

    
98

    
99
class MininetRunner( object ):
100
    "Build, setup, and run Mininet."
101

    
102
    def __init__( self ):
103
        "Init."
104
        self.options = None
105
        self.args = None  # May be used someday for more CLI scripts
106
        self.validate = None
107

    
108
        self.parseArgs()
109
        self.setup()
110
        self.begin()
111

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

    
125
    def parseCustomFile( self, fileName ):
126
        "Parse custom file and add params before parsing cmd-line options."
127
        custom = {}
128
        if os.path.isfile( fileName ):
129
            execfile( fileName, custom, custom )
130
            for name in custom:
131
                self.setCustom( name, custom[ name ] )
132
        else:
133
            raise Exception( 'could not find custom file: %s' % fileName )
134

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

    
147
        opts = OptionParser()
148
        addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
149
        addDictOption( opts, HOSTS, HOSTDEF, 'host' )
150
        addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
151

    
152
        opts.add_option( '--topo', type='string', default=TOPODEF,
153
                        help='[' + ' '.join( TOPOS.keys() ) + '],arg1,arg2,'
154
                        '...argN')
155
        opts.add_option( '--clean', '-c', action='store_true',
156
                        default=False, help='clean and exit' )
157
        opts.add_option( '--custom', type='string', default=None,
158
                        help='read custom topo and node params from .py file' )
159
        opts.add_option( '--test', type='choice', choices=TESTS,
160
                        default=TESTS[ 0 ],
161
                        help='[' + ' '.join( TESTS ) + ']' )
162
        opts.add_option( '--xterms', '-x', action='store_true',
163
                        default=False, help='spawn xterms for each node' )
164
        opts.add_option( '--mac', action='store_true',
165
                        default=False, help='set MACs equal to DPIDs' )
166
        opts.add_option( '--arp', action='store_true',
167
                        default=False, help='set all-pairs ARP entries' )
168
        opts.add_option( '--verbosity', '-v', type='choice',
169
                        choices=LEVELS.keys(), default = 'info',
170
                        help = '[' + ' '.join( LEVELS.keys() ) + ']' )
171
        opts.add_option( '--ip', type='string', default='127.0.0.1',
172
                        help='[ip address as a dotted decimal string for a'
173
                        'remote controller]' )
174
        opts.add_option( '--port', type='int', default=6633,
175
                        help='[port integer for a listening remote'
176
                        ' controller]' )
177
        opts.add_option( '--innamespace', action='store_true',
178
                        default=False, help='sw and ctrl in namespace?' )
179
        opts.add_option( '--listenport', type='int', default=6634,
180
                        help='[base port for passive switch listening'
181
                        ' controller]' )
182
        opts.add_option( '--nolistenport', action='store_true',
183
                        default=False, help="don't use passive listening port")
184
        opts.add_option( '--pre', type='string', default=None,
185
                        help='[CLI script to run before tests]' )
186
        opts.add_option( '--post', type='string', default=None,
187
                        help='[CLI script to run after tests]' )
188
        opts.add_option( '--prefixlen', type='int', default=8,
189
                        help='[prefix length (e.g. /8) for automatic '
190
                        'network configuration]' )
191

    
192
        self.options, self.args = opts.parse_args()
193

    
194
    def setup( self ):
195
        "Setup and validate environment."
196

    
197
        # set logging verbosity
198
        if LEVELS[self.options.verbosity] > LEVELS['output']:
199
            print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
200
                    'output!\n'
201
                    'Please restart Mininet with -v [debug, info, output].' )
202
        lg.setLogLevel( self.options.verbosity )
203

    
204
        # validate environment setup
205
        init()
206

    
207
    def begin( self ):
208
        "Create and run mininet."
209

    
210
        if self.options.clean:
211
            cleanup()
212
            exit()
213

    
214
        start = time.time()
215

    
216
        topo = buildTopo( self.options.topo )
217
        switch = SWITCHES[ self.options.switch ]
218
        host = HOSTS[ self.options.host ]
219
        controller = CONTROLLERS[ self.options.controller ]
220
        if self.options.controller == 'remote':
221
            controller = lambda a: RemoteController( a,
222
                             defaultIP=self.options.ip,
223
                             port=self.options.port )
224

    
225
        if self.validate:
226
            self.validate( self.options )
227

    
228
        # We should clarify what this is actually for...
229
        # It seems like it should be default values for the
230
        # *data* network, so it may be misnamed.
231
        controllerParams = ControllerParams( '10.0.0.0',
232
            self.options.prefixlen)
233

    
234
        inNamespace = self.options.innamespace
235
        xterms = self.options.xterms
236
        mac = self.options.mac
237
        arp = self.options.arp
238
        listenPort = None
239
        if not self.options.nolistenport:
240
            listenPort = self.options.listenport
241
        mn = Mininet( topo, switch, host, controller, controllerParams,
242
                     inNamespace=inNamespace,
243
                     xterms=xterms, autoSetMacs=mac,
244
                     autoStaticArp=arp, listenPort=listenPort )
245

    
246
        if self.options.pre:
247
            CLI( mn, script=self.options.pre )
248

    
249
        test = self.options.test
250
        test = ALTSPELLING.get( test, test )
251

    
252
        mn.start()
253

    
254
        if test == 'none':
255
            pass
256
        elif test == 'all':
257
            mn.start()
258
            mn.ping()
259
            mn.iperf()
260
        elif test == 'cli':
261
            CLI( mn )
262
        elif test != 'build':
263
            getattr( mn, test )()
264

    
265
        if self.options.post:
266
            CLI( mn, script=self.options.post )
267

    
268
        mn.stop()
269

    
270
        elapsed = float( time.time() - start )
271
        info( 'completed in %0.3f seconds\n' % elapsed )
272

    
273

    
274
if __name__ == "__main__":
275
    MininetRunner()