blob: 0fb5bae351e9ed120d69ec8955e3aa52b7235e11 [file] [log] [blame]
carlosmscabralf40ecd12013-02-01 18:15:58 -02001#!/usr/bin/env python
2
3"""
4Mininet runner
5author: Brandon Heller (brandonh@stanford.edu)
6
7To see options:
8 sudo mn -h
9
10Example to pull custom params (topo, switch, etc.) from a file:
11 sudo mn --custom ~/mininet/custom/custom_example.py
12"""
13
14from optparse import OptionParser
15import os
16import sys
17import time
18
19# Fix setuptools' evil madness, and open up (more?) security holes
20if 'PYTHONPATH' in os.environ:
21 sys.path = os.environ[ 'PYTHONPATH' ].split( ':' ) + sys.path
22
23from mininet.clean import cleanup
24from mininet.cli import CLI
25from mininet.log import lg, LEVELS, info
26from mininet.net import Mininet, MininetWithControlNet, VERSION
27from mininet.node import ( Host, CPULimitedHost, Controller, OVSController,
28 NOX, RemoteController, UserSwitch, OVSKernelSwitch,
29 OVSLegacyKernelSwitch )
30from mininet.link import Link, TCLink
31from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo
32from mininet.topolib import TreeTopo
33from mininet.util import custom, customConstructor
34from mininet.util import buildTopo
35
36
37# built in topologies, created only when run
38TOPODEF = 'minimal'
39TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ),
40 'linear': LinearTopo,
41 'reversed': SingleSwitchReversedTopo,
42 'single': SingleSwitchTopo,
43 'tree': TreeTopo }
44
45SWITCHDEF = 'ovsk'
46SWITCHES = { 'user': UserSwitch,
47 'ovsk': OVSKernelSwitch,
48 'ovsl': OVSLegacyKernelSwitch }
49
50HOSTDEF = 'proc'
51HOSTS = { 'proc': Host,
52 'rt': custom( CPULimitedHost, sched='rt' ),
53 'cfs': custom( CPULimitedHost, sched='cfs' ) }
54
55CONTROLLERDEF = 'ovsc'
56CONTROLLERS = { 'ref': Controller,
57 'ovsc': OVSController,
58 'nox': NOX,
59 'remote': RemoteController,
60 'none': lambda name: None }
61
62LINKDEF = 'default'
63LINKS = { 'default': Link,
64 'tc': TCLink }
65
66
67# optional tests to run
68TESTS = [ 'cli', 'build', 'pingall', 'pingpair', 'iperf', 'all', 'iperfudp',
69 'none' ]
70
71ALTSPELLING = { 'pingall': 'pingAll',
72 'pingpair': 'pingPair',
73 'iperfudp': 'iperfUdp',
74 'iperfUDP': 'iperfUdp',
75 'prefixlen': 'prefixLen' }
76
77
78def addDictOption( opts, choicesDict, default, name, helpStr=None ):
79 """Convenience function to add choices dicts to OptionParser.
80 opts: OptionParser instance
81 choicesDict: dictionary of valid choices, must include default
82 default: default choice key
83 name: long option name
84 help: string"""
85 if default not in choicesDict:
86 raise Exception( 'Invalid default %s for choices dict: %s' %
87 ( default, name ) )
88 if not helpStr:
89 helpStr = ( '|'.join( sorted( choicesDict.keys() ) ) +
90 '[,param=value...]' )
91 opts.add_option( '--' + name,
92 type='string',
93 default = default,
94 help = helpStr )
95
96
97def version( *_args ):
98 "Print Mininet version and exit"
99 print "%s" % VERSION
100 exit()
101
102class MininetRunner( object ):
103 "Build, setup, and run Mininet."
104
105 def __init__( self ):
106 "Init."
107 self.options = None
108 self.args = None # May be used someday for more CLI scripts
109 self.validate = None
110
111 self.parseArgs()
112 self.setup()
113 self.begin()
114
115 def setCustom( self, name, value ):
116 "Set custom parameters for MininetRunner."
117 if name in ( 'topos', 'switches', 'hosts', 'controllers' ):
118 # Update dictionaries
119 param = name.upper()
120 globals()[ param ].update( value )
121 elif name == 'validate':
122 # Add custom validate function
123 self.validate = value
124 else:
125 # Add or modify global variable or class
126 globals()[ name ] = value
127
128 def parseCustomFile( self, fileName ):
129 "Parse custom file and add params before parsing cmd-line options."
130 customs = {}
131 if os.path.isfile( fileName ):
132 execfile( fileName, customs, customs )
133 for name, val in customs.iteritems():
134 self.setCustom( name, val )
135 else:
136 raise Exception( 'could not find custom file: %s' % fileName )
137
138 def parseArgs( self ):
139 """Parse command-line args and return options object.
140 returns: opts parse options dict"""
141 if '--custom' in sys.argv:
142 index = sys.argv.index( '--custom' )
143 if len( sys.argv ) > index + 1:
144 filename = sys.argv[ index + 1 ]
145 self.parseCustomFile( filename )
146 else:
147 raise Exception( 'Custom file name not found' )
148
149 desc = ( "The %prog utility creates Mininet network from the\n"
150 "command line. It can create parametrized topologies,\n"
151 "invoke the Mininet CLI, and run tests." )
152
153 usage = ( '%prog [options]\n'
154 '(type %prog -h for details)' )
155
156 opts = OptionParser( description=desc, usage=usage )
157 addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' )
158 addDictOption( opts, HOSTS, HOSTDEF, 'host' )
159 addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' )
160 addDictOption( opts, LINKS, LINKDEF, 'link' )
161 addDictOption( opts, TOPOS, TOPODEF, 'topo' )
162
163 opts.add_option( '--clean', '-c', action='store_true',
164 default=False, help='clean and exit' )
165 opts.add_option( '--custom', type='string', default=None,
166 help='read custom topo and node params from .py' +
167 'file' )
168 opts.add_option( '--test', type='choice', choices=TESTS,
169 default=TESTS[ 0 ],
170 help='|'.join( TESTS ) )
171 opts.add_option( '--xterms', '-x', action='store_true',
172 default=False, help='spawn xterms for each node' )
173 opts.add_option( '--ipbase', '-i', type='string', default='10.0.0.0/8',
174 help='base IP address for hosts' )
175 opts.add_option( '--mac', action='store_true',
176 default=False, help='automatically set host MACs' )
177 opts.add_option( '--arp', action='store_true',
178 default=False, help='set all-pairs ARP entries' )
179 opts.add_option( '--verbosity', '-v', type='choice',
180 choices=LEVELS.keys(), default = 'info',
181 help = '|'.join( LEVELS.keys() ) )
182 opts.add_option( '--innamespace', action='store_true',
183 default=False, help='sw and ctrl in namespace?' )
184 opts.add_option( '--listenport', type='int', default=6634,
185 help='base port for passive switch listening' )
186 opts.add_option( '--nolistenport', action='store_true',
187 default=False, help="don't use passive listening " +
188 "port")
189 opts.add_option( '--pre', type='string', default=None,
190 help='CLI script to run before tests' )
191 opts.add_option( '--post', type='string', default=None,
192 help='CLI script to run after tests' )
193 opts.add_option( '--prefixlen', type='int', default=8,
194 help='prefix length (e.g. /8) for automatic '
195 'network configuration' )
196 opts.add_option( '--pin', action='store_true',
197 default=False, help="pin hosts to CPU cores "
198 "(requires --host cfs or --host rt)" )
199 opts.add_option( '--version', action='callback', callback=version )
200
201 self.options, self.args = opts.parse_args()
202
203 def setup( self ):
204 "Setup and validate environment."
205
206 # set logging verbosity
207 if LEVELS[self.options.verbosity] > LEVELS['output']:
208 print ( '*** WARNING: selected verbosity level (%s) will hide CLI '
209 'output!\n'
210 'Please restart Mininet with -v [debug, info, output].'
211 % self.options.verbosity )
212 lg.setLogLevel( self.options.verbosity )
213
214 def begin( self ):
215 "Create and run mininet."
216
217 if self.options.clean:
218 cleanup()
219 exit()
220
221 start = time.time()
222
223 topo = buildTopo( TOPOS, self.options.topo )
224 switch = customConstructor( SWITCHES, self.options.switch )
225 host = customConstructor( HOSTS, self.options.host )
226 controller = customConstructor( CONTROLLERS, self.options.controller )
227 link = customConstructor( LINKS, self.options.link )
228
229 if self.validate:
230 self.validate( self.options )
231
232 inNamespace = self.options.innamespace
233 Net = MininetWithControlNet if inNamespace else Mininet
234 ipBase = self.options.ipbase
235 xterms = self.options.xterms
236 mac = self.options.mac
237 arp = self.options.arp
238 pin = self.options.pin
239 listenPort = None
240 if not self.options.nolistenport:
241 listenPort = self.options.listenport
242 mn = Net( topo=topo,
243 switch=switch, host=host, controller=controller,
244 link=link,
245 ipBase=ipBase,
246 inNamespace=inNamespace,
247 xterms=xterms, autoSetMacs=mac,
248 autoStaticArp=arp, autoPinCpus=pin,
249 listenPort=listenPort )
250
251 if self.options.pre:
252 CLI( mn, script=self.options.pre )
253
254 test = self.options.test
255 test = ALTSPELLING.get( test, test )
256
257 mn.start()
258
259 if test == 'none':
260 pass
261 elif test == 'all':
262 mn.start()
263 mn.ping()
264 mn.iperf()
265 elif test == 'cli':
266 CLI( mn )
267 elif test != 'build':
268 getattr( mn, test )()
269
270 if self.options.post:
271 CLI( mn, script=self.options.post )
272
273 mn.stop()
274
275 elapsed = float( time.time() - start )
276 info( 'completed in %0.3f seconds\n' % elapsed )
277
278
279if __name__ == "__main__":
280 MininetRunner()