blob: adb4a10a948b9de57560ad32b285196a86aaf446 [file] [log] [blame]
Thomas Vachuska9ee49792016-03-01 16:51:14 -08001#!/usr/bin/python
Brian O'Connord6c73fb2016-03-04 15:24:52 -08002import itertools
Carmelo Casconec52a4b12016-10-24 16:47:17 +02003import os
Brian O'Connore9b4dd72016-03-05 01:07:18 -08004import signal
Carmelo Casconec52a4b12016-10-24 16:47:17 +02005import sys
6from argparse import ArgumentParser
7from subprocess import call
Brian O'Connore9b4dd72016-03-05 01:07:18 -08008from threading import Thread
Carmelo Casconec52a4b12016-10-24 16:47:17 +02009from time import sleep
Thomas Vachuska9ee49792016-03-01 16:51:14 -080010
Brian O'Connore9b4dd72016-03-05 01:07:18 -080011import gratuitousArp
Carmelo Casconec52a4b12016-10-24 16:47:17 +020012from mininet.cli import CLI
13from mininet.examples.controlnet import MininetFacade
14from mininet.link import TCLink
15from mininet.log import info, output, error
16from mininet.log import setLogLevel
17from mininet.net import Mininet
18from mininet.node import RemoteController, Node
19
Brian O'Connore9b4dd72016-03-05 01:07:18 -080020ARP_PATH = gratuitousArp.__file__.replace('.pyc', '.py')
Thomas Vachuska9ee49792016-03-01 16:51:14 -080021
Brian O'Connore9b4dd72016-03-05 01:07:18 -080022class ONOSMininet( Mininet ):
Thomas Vachuska9ee49792016-03-01 16:51:14 -080023
24 def __init__( self, controllers=[], gratuitousArp=True, build=True, *args, **kwargs ):
25 """Create Mininet object for ONOS.
26 controllers: List of controller IP addresses
27 gratuitousArp: Send an ARP from each host to aid controller's host discovery"""
Thomas Vachuska9ee49792016-03-01 16:51:14 -080028
29 # delay building for a second
30 kwargs[ 'build' ] = False
31
32 Mininet.__init__(self, *args, **kwargs )
33
34 self.gratArp = gratuitousArp
Thomas Vachuska9ee49792016-03-01 16:51:14 -080035
Carmelo Casconec52a4b12016-10-24 16:47:17 +020036 # If a controller is not provided, use list of remote controller IPs instead.
37 if 'controller' not in kwargs or not kwargs['controller']:
38 info ( '*** Adding controllers\n' )
39 ctrl_count = 0
40 for controllerIP in controllers:
41 self.addController( 'c%d' % ctrl_count, RemoteController, ip=controllerIP )
42 info( ' c%d (%s)\n' % ( ctrl_count, controllerIP ) )
43 ctrl_count = ctrl_count + 1
Thomas Vachuska9ee49792016-03-01 16:51:14 -080044
45 if self.topo and build:
46 self.build()
47
48 def start( self ):
49 Mininet.start( self )
50 if self.gratArp:
Brian O'Connore9b4dd72016-03-05 01:07:18 -080051 self.waitConnected( timeout=5 )
Thomas Vachuska552c3822018-02-07 14:08:48 -080052 sleep(2)
Thomas Vachuska9ee49792016-03-01 16:51:14 -080053 info ( '*** Sending a gratuitious ARP from each host\n' )
54 self.gratuitousArp()
55
Brian O'Connore9b4dd72016-03-05 01:07:18 -080056 def verifyHosts( self, hosts ):
57 for i in range( len( hosts ) ):
58 if isinstance( hosts[i], str):
59 if hosts[i] in self:
60 hosts[i] = self[ hosts[i] ]
Brian O'Connord6c73fb2016-03-04 15:24:52 -080061 else:
Brian O'Connore9b4dd72016-03-05 01:07:18 -080062 info( '*** ERROR: %s is not a host\n' % hosts[i] )
63 del hosts[i]
64 elif not isinstance( hosts[i], Node):
65 del hosts[i]
66
67 def gratuitousArp( self, hosts=[] ):
68 "Send an ARP from each host to aid controller's host discovery; fallback to ping if necessary"
69 if not hosts:
70 hosts = self.hosts
71 self.verifyHosts( hosts )
72
73 for host in hosts:
74 info( '%s ' % host.name )
75 info( host.cmd( ARP_PATH ) )
Brian O'Connord6c73fb2016-03-04 15:24:52 -080076 info ( '\n' )
Thomas Vachuska9ee49792016-03-01 16:51:14 -080077
78 def pingloop( self ):
79 "Loop forever pinging the full mesh of hosts"
80 setLogLevel( 'error' )
81 try:
82 while True:
83 self.ping()
84 finally:
85 setLogLevel( 'info' )
86
Brian O'Connord6c73fb2016-03-04 15:24:52 -080087 def bgIperf( self, hosts=[], seconds=10 ):
Brian O'Connore9b4dd72016-03-05 01:07:18 -080088 self.verifyHosts( hosts )
Brian O'Connord6c73fb2016-03-04 15:24:52 -080089 servers = [ host.popen("iperf -s") for host in hosts ]
90
91 clients = []
Brian O'Connore9b4dd72016-03-05 01:07:18 -080092 for s, d in itertools.combinations(hosts, 2):
93 info ( '%s <--> %s\n' % ( s.name, d.name ))
94 cmd = 'iperf -c %s -t %s -y csv' % (d.IP(), seconds)
95 p = s.popen(cmd)
96 p.s = s.name
97 p.d = d.name
98 clients.append(p)
Brian O'Connord6c73fb2016-03-04 15:24:52 -080099
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800100 def handler (_signum, _frame):
101 raise BackgroundException()
102 oldSignal = signal.getsignal(signal.SIGTSTP)
103 signal.signal(signal.SIGTSTP, handler)
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800104
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800105 def finish( verbose=True ):
106 for c in clients:
107 out, err = c.communicate()
108 if verbose:
109 if err:
110 info( err )
111 else:
112 bw = out.split( ',' )[8]
113 info( '%s <--> %s: %s\n' % ( c.s, c.d, formatBw(bw) ) )
114 for s in servers:
115 s.terminate()
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800116
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800117 try:
118 info ( 'Press ^Z to continue in background or ^C to abort\n')
119 progress( seconds )
120 finish()
121 except KeyboardInterrupt:
122 for c in clients:
123 c.terminate()
124 for s in servers:
125 s.terminate()
126 except BackgroundException:
127 info( '\n*** Continuing in background...\n' )
128 t = Thread( target=finish, args=[ False ] )
129 t.start()
130 finally:
131 #Disable custom background signal
132 signal.signal(signal.SIGTSTP, oldSignal)
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800133
134def progress(t):
135 while t > 0:
136 sys.stdout.write( '.' )
137 t -= 1
138 sys.stdout.flush()
139 sleep(1)
140 print
141
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800142def formatBw( bw ):
143 bw = float(bw)
144 if bw > 1000:
145 bw /= 1000
146 if bw > 1000:
147 bw /= 1000
148 if bw > 1000:
149 bw /= 1000
150 return '%.2f Gbps' % bw
151 return '%.2f Mbps' % bw
152 return '%.2f Kbps' % bw
153 return '%.2f bps' % bw
Thomas Vachuska9ee49792016-03-01 16:51:14 -0800154
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800155class BackgroundException( Exception ):
156 pass
157
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200158
159def get_mn(mn):
160 if isinstance(mn, ONOSMininet):
161 return mn
162 elif isinstance(mn, MininetFacade):
163 # There's more Mininet objects instantiated (e.g. one for the control network in onos.py).
164 for net in mn.nets:
165 if isinstance(net, ONOSMininet):
166 return net
167 return None
168
169
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800170def do_bgIperf( self, line ):
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800171 args = line.split()
172 if not args:
173 output( 'Provide a list of hosts.\n' )
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800174
175 #Try to parse the '-t' argument as the number of seconds
176 seconds = 10
177 for i, arg in enumerate(args):
178 if arg == '-t':
179 if i + 1 < len(args):
180 try:
181 seconds = int(args[i + 1])
182 except ValueError:
183 error( 'Could not parse number of seconds: %s', args[i+1] )
184 del(args[i+1])
185 del args[i]
186
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800187 hosts = []
188 err = False
189 for arg in args:
190 if arg not in self.mn:
191 err = True
192 error( "node '%s' not in network\n" % arg )
193 else:
194 hosts.append( self.mn[ arg ] )
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200195 mn = get_mn( self.mn )
196 if "bgIperf" in dir( mn ) and not err:
197 mn.bgIperf( hosts, seconds=seconds )
198 else:
199 output('Background Iperf is not supported.\n')
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800200
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800201def do_gratuitousArp( self, line ):
202 args = line.split()
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200203 mn = get_mn(self.mn)
204 if "gratuitousArp" in dir( mn ):
205 mn.gratuitousArp( args )
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800206 else:
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800207 output( 'Gratuitous ARP is not supported.\n' )
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800208
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800209CLI.do_bgIperf = do_bgIperf
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800210CLI.do_gratuitousArp = do_gratuitousArp
211
Luca Prete3d402dc2016-12-13 16:25:03 -0800212def parse_args():
213 parser = ArgumentParser(description='ONOS Mininet')
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200214 parser.add_argument('--cluster-size', help='Starts an ONOS cluster with the given number of instances',
215 type=int, action='store', dest='clusterSize', required=False, default=0)
216 parser.add_argument('--netcfg', help='Relative path of the JSON file to be used with netcfg',
217 type=str, action='store', dest='netcfgJson', required=False, default='')
218 parser.add_argument('ipAddrs', metavar='IP', type=str, nargs='*',
219 help='List of controller IP addresses', default=[])
Luca Prete3d402dc2016-12-13 16:25:03 -0800220 return parser.parse_args()
221
222def run( topo, controllers=None, link=TCLink, autoSetMacs=True):
223 if not topo:
224 print 'Need to provide a topology'
225 exit(1)
226
227 args = parse_args()
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200228
229 if not controllers and len(args.ipAddrs) > 0:
230 controllers = args.ipAddrs
231
232 if not controllers and args.clusterSize < 1:
233 print 'Need to provide a list of controller IPs, or define a cluster size.'
Thomas Vachuska9ee49792016-03-01 16:51:14 -0800234 exit( 1 )
235
236 setLogLevel( 'info' )
237
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200238 if args.clusterSize > 0:
239 if 'ONOS_ROOT' not in os.environ:
240 print "Environment var $ONOS_ROOT not set (needed to import onos.py)"
241 exit( 1 )
242 sys.path.append(os.environ["ONOS_ROOT"] + "/tools/dev/mininet")
243 from onos import ONOSCluster, ONOSOVSSwitch, ONOSCLI
244 controller = ONOSCluster('c0', args.clusterSize)
245 onosAddr = controller.nodes()[0].IP()
246 net = ONOSMininet( topo=topo, controller=controller, switch=ONOSOVSSwitch, link=link,
247 autoSetMacs=autoSetMacs )
248 cli = ONOSCLI
249 else:
250 onosAddr = controllers[0]
251 net = ONOSMininet(topo=topo, controllers=controllers, link=link, autoSetMacs=autoSetMacs)
252 cli = CLI
253
Thomas Vachuska9ee49792016-03-01 16:51:14 -0800254 net.start()
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200255
256 if len(args.netcfgJson) > 0:
257 if not os.path.isfile(args.netcfgJson):
258 error('*** WARNING no such netcfg file: %s\n' % args.netcfgJson)
259 else:
260 info('*** Setting netcfg: %s\n' % args.netcfgJson)
261 call(("onos-netcfg", onosAddr, args.netcfgJson))
262
263 cli( net )
Thomas Vachuska9ee49792016-03-01 16:51:14 -0800264 net.stop()