mininet / bin / mn @ 68f5925e
History | View | Annotate | Download (8.55 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.log import lg, LEVELS, info |
21 |
from mininet.net import Mininet, init |
22 |
from mininet.node import KernelSwitch, Host, Controller, ControllerParams, NOX |
23 |
from mininet.node import RemoteController, UserSwitch, OVSKernelSwitch |
24 |
from mininet.topo import SingleSwitchTopo, LinearTopo, SingleSwitchReversedTopo |
25 |
from mininet.topolib import TreeTopo |
26 |
from mininet.util import makeNumeric |
27 |
|
28 |
# built in topologies, created only when run |
29 |
TOPODEF = 'minimal' |
30 |
TOPOS = { 'minimal': lambda: SingleSwitchTopo( k=2 ), |
31 |
'linear': LinearTopo, |
32 |
'reversed': SingleSwitchReversedTopo, |
33 |
'single': SingleSwitchTopo, |
34 |
'tree': TreeTopo } |
35 |
|
36 |
SWITCHDEF = 'kernel' |
37 |
SWITCHES = { 'kernel': KernelSwitch, |
38 |
'user': UserSwitch, |
39 |
'ovsk': OVSKernelSwitch } |
40 |
|
41 |
HOSTDEF = 'process' |
42 |
HOSTS = { 'process': Host } |
43 |
|
44 |
CONTROLLERDEF = 'ref' |
45 |
# a and b are the name and inNamespace params. |
46 |
CONTROLLERS = { 'ref': Controller, |
47 |
'nox_dump': lambda name: NOX( name, 'packetdump' ), |
48 |
'nox_pysw': lambda name: NOX( name, 'pyswitch' ), |
49 |
'remote': lambda name: None, |
50 |
'none': lambda name: None } |
51 |
|
52 |
# optional tests to run |
53 |
TESTS = [ 'cli', 'build', 'pingAll', 'pingPair', 'iperf', 'all', 'iperfUdp', |
54 |
'none' ] |
55 |
|
56 |
|
57 |
def buildTopo( topo ): |
58 |
"Create topology from string with format (object, arg1, arg2,...)." |
59 |
topo_split = topo.split( ',' ) |
60 |
topo_name = topo_split[ 0 ] |
61 |
topo_params = topo_split[ 1: ] |
62 |
|
63 |
# Convert int and float args; removes the need for every topology to |
64 |
# be flexible with input arg formats. |
65 |
topo_seq_params = [ s for s in topo_params if '=' not in s ] |
66 |
topo_seq_params = [ makeNumeric( s ) for s in topo_seq_params ] |
67 |
topo_kw_params = {} |
68 |
for s in [ p for p in topo_params if '=' in p ]: |
69 |
key, val = s.split( '=' ) |
70 |
topo_kw_params[ key ] = makeNumeric( val ) |
71 |
|
72 |
if topo_name not in TOPOS.keys(): |
73 |
raise Exception( 'Invalid topo_name %s' % topo_name ) |
74 |
return TOPOS[ topo_name ]( *topo_seq_params, **topo_kw_params ) |
75 |
|
76 |
|
77 |
def addDictOption( opts, choicesDict, default, name, helpStr=None ): |
78 |
"""Convenience function to add choices dicts to OptionParser. |
79 |
opts: OptionParser instance |
80 |
choicesDict: dictionary of valid choices, must include default |
81 |
default: default choice key |
82 |
name: long option name |
83 |
help: string""" |
84 |
if default not in choicesDict: |
85 |
raise Exception( 'Invalid default %s for choices dict: %s' % |
86 |
( default, name ) ) |
87 |
if not helpStr: |
88 |
helpStr = '[' + ' '.join( choicesDict.keys() ) + ']' |
89 |
opts.add_option( '--' + name, |
90 |
type='choice', |
91 |
choices=choicesDict.keys(), |
92 |
default = default, |
93 |
help = helpStr ) |
94 |
|
95 |
|
96 |
class MininetRunner( object ): |
97 |
"Build, setup, and run Mininet." |
98 |
|
99 |
def __init__( self ): |
100 |
"Init." |
101 |
self.options = None |
102 |
self.validate = None |
103 |
|
104 |
self.parseArgs() |
105 |
self.setup() |
106 |
self.begin() |
107 |
|
108 |
def setCustom( self, name, value ): |
109 |
"Set custom parameters for MininetRunner." |
110 |
if name in ( 'topos', 'switches', 'hosts', 'controllers' ): |
111 |
# Update dictionaries |
112 |
param = name.upper() |
113 |
globals()[ param ].update( value ) |
114 |
elif name == 'validate': |
115 |
# Add custom validate function |
116 |
self.validate = value |
117 |
else: |
118 |
# Add or modify global variable or class |
119 |
globals()[ name ] = value |
120 |
|
121 |
def parseCustomFile( self, fileName ): |
122 |
"Parse custom file and add params before parsing cmd-line options." |
123 |
custom = {} |
124 |
if os.path.isfile( fileName ): |
125 |
execfile( fileName, custom, custom ) |
126 |
for name in custom: |
127 |
self.setCustom( name, custom[ name ] ) |
128 |
else: |
129 |
raise Exception( 'could not find custom file: %s' % fileName ) |
130 |
|
131 |
def parseArgs( self ): |
132 |
"""Parse command-line args and return options object. |
133 |
returns: opts parse options dict""" |
134 |
if '--custom' in sys.argv: |
135 |
print "custom in sys.argv" |
136 |
index = sys.argv.index( '--custom' ) |
137 |
if len( sys.argv ) > index + 1: |
138 |
custom = sys.argv[ index + 1 ] |
139 |
self.parseCustomFile( custom ) |
140 |
else: |
141 |
raise Exception( 'Custom file name not found' ) |
142 |
|
143 |
opts = OptionParser() |
144 |
addDictOption( opts, SWITCHES, SWITCHDEF, 'switch' ) |
145 |
addDictOption( opts, HOSTS, HOSTDEF, 'host' ) |
146 |
addDictOption( opts, CONTROLLERS, CONTROLLERDEF, 'controller' ) |
147 |
|
148 |
opts.add_option( '--topo', type='string', default=TOPODEF, |
149 |
help='[' + ' '.join( TOPOS.keys() ) + '],arg1,arg2,' |
150 |
'...argN') |
151 |
opts.add_option( '--clean', '-c', action='store_true', |
152 |
default=False, help='clean and exit' ) |
153 |
opts.add_option( '--custom', type='string', default=None, |
154 |
help='read custom topo and node params from .py file' ) |
155 |
opts.add_option( '--test', type='choice', choices=TESTS, |
156 |
default=TESTS[ 0 ], |
157 |
help='[' + ' '.join( TESTS ) + ']' ) |
158 |
opts.add_option( '--xterms', '-x', action='store_true', |
159 |
default=False, help='spawn xterms for each node' ) |
160 |
opts.add_option( '--mac', action='store_true', |
161 |
default=False, help='set MACs equal to DPIDs' ) |
162 |
opts.add_option( '--arp', action='store_true', |
163 |
default=False, help='set all-pairs ARP entries' ) |
164 |
opts.add_option( '--verbosity', '-v', type='choice', |
165 |
choices=LEVELS.keys(), default = 'info', |
166 |
help = '[' + ' '.join( LEVELS.keys() ) + ']' ) |
167 |
opts.add_option( '--ip', type='string', default='127.0.0.1', |
168 |
help='[ip address as a dotted decimal string for a' |
169 |
'remote controller]' ) |
170 |
opts.add_option( '--port', type='int', default=6633, |
171 |
help='[port integer for a listening remote' |
172 |
' controller]' ) |
173 |
opts.add_option( '--inNamespace', action='store_true', |
174 |
default=False, help='sw and ctrl in namespace?' ) |
175 |
self.options = opts.parse_args()[ 0 ] |
176 |
|
177 |
def setup( self ): |
178 |
"Setup and validate environment." |
179 |
|
180 |
# set logging verbosity |
181 |
if LEVELS[self.options.verbosity] > LEVELS['output']: |
182 |
print ( '*** WARNING: selected verbosity level (%s) will hide CLI ' |
183 |
'output!\n' |
184 |
'Please restart Mininet with -v [debug, info, output].' ) |
185 |
lg.setLogLevel( self.options.verbosity ) |
186 |
|
187 |
# validate environment setup |
188 |
init() |
189 |
|
190 |
def begin( self ): |
191 |
"Create and run mininet." |
192 |
|
193 |
if self.options.clean: |
194 |
cleanup() |
195 |
exit() |
196 |
|
197 |
start = time.time() |
198 |
|
199 |
topo = buildTopo( self.options.topo ) |
200 |
switch = SWITCHES[ self.options.switch ] |
201 |
host = HOSTS[ self.options.host ] |
202 |
controller = CONTROLLERS[ self.options.controller ] |
203 |
if self.options.controller == 'remote': |
204 |
controller = lambda a: RemoteController( a, |
205 |
defaultIP=self.options.ip, |
206 |
port=self.options.port ) |
207 |
|
208 |
if self.validate: |
209 |
self.validate( self.options ) |
210 |
|
211 |
controllerParams = ControllerParams( '10.0.0.0', 8 ) |
212 |
inNamespace = self.options.inNamespace |
213 |
xterms = self.options.xterms |
214 |
mac = self.options.mac |
215 |
arp = self.options.arp |
216 |
mn = Mininet( topo, switch, host, controller, controllerParams, |
217 |
inNamespace=inNamespace, |
218 |
xterms=xterms, autoSetMacs=mac, |
219 |
autoStaticArp=arp ) |
220 |
|
221 |
test = self.options.test |
222 |
if test == 'none': |
223 |
mn.start() |
224 |
mn.stop() |
225 |
elif test == 'cli': |
226 |
mn.interact() |
227 |
elif test == 'all': |
228 |
mn.start() |
229 |
mn.ping() |
230 |
mn.iperf() |
231 |
mn.stop() |
232 |
elif test != 'build': |
233 |
mn.run( getattr( mn, test ) ) |
234 |
|
235 |
elapsed = float( time.time() - start ) |
236 |
info( 'completed in %0.3f seconds\n' % elapsed ) |
237 |
|
238 |
|
239 |
if __name__ == "__main__": |
240 |
MininetRunner() |