blob: f8e4f6a34ca7415acebf6a46250ad7988ed6a6cc [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 Vachuska9ee49792016-03-01 16:51:14 -080052 info ( '*** Sending a gratuitious ARP from each host\n' )
53 self.gratuitousArp()
54
Brian O'Connore9b4dd72016-03-05 01:07:18 -080055 def verifyHosts( self, hosts ):
56 for i in range( len( hosts ) ):
57 if isinstance( hosts[i], str):
58 if hosts[i] in self:
59 hosts[i] = self[ hosts[i] ]
Brian O'Connord6c73fb2016-03-04 15:24:52 -080060 else:
Brian O'Connore9b4dd72016-03-05 01:07:18 -080061 info( '*** ERROR: %s is not a host\n' % hosts[i] )
62 del hosts[i]
63 elif not isinstance( hosts[i], Node):
64 del hosts[i]
65
66 def gratuitousArp( self, hosts=[] ):
67 "Send an ARP from each host to aid controller's host discovery; fallback to ping if necessary"
68 if not hosts:
69 hosts = self.hosts
70 self.verifyHosts( hosts )
71
72 for host in hosts:
73 info( '%s ' % host.name )
74 info( host.cmd( ARP_PATH ) )
Brian O'Connord6c73fb2016-03-04 15:24:52 -080075 info ( '\n' )
Thomas Vachuska9ee49792016-03-01 16:51:14 -080076
77 def pingloop( self ):
78 "Loop forever pinging the full mesh of hosts"
79 setLogLevel( 'error' )
80 try:
81 while True:
82 self.ping()
83 finally:
84 setLogLevel( 'info' )
85
Brian O'Connord6c73fb2016-03-04 15:24:52 -080086 def bgIperf( self, hosts=[], seconds=10 ):
Brian O'Connore9b4dd72016-03-05 01:07:18 -080087 self.verifyHosts( hosts )
Brian O'Connord6c73fb2016-03-04 15:24:52 -080088 servers = [ host.popen("iperf -s") for host in hosts ]
89
90 clients = []
Brian O'Connore9b4dd72016-03-05 01:07:18 -080091 for s, d in itertools.combinations(hosts, 2):
92 info ( '%s <--> %s\n' % ( s.name, d.name ))
93 cmd = 'iperf -c %s -t %s -y csv' % (d.IP(), seconds)
94 p = s.popen(cmd)
95 p.s = s.name
96 p.d = d.name
97 clients.append(p)
Brian O'Connord6c73fb2016-03-04 15:24:52 -080098
Brian O'Connore9b4dd72016-03-05 01:07:18 -080099 def handler (_signum, _frame):
100 raise BackgroundException()
101 oldSignal = signal.getsignal(signal.SIGTSTP)
102 signal.signal(signal.SIGTSTP, handler)
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800103
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800104 def finish( verbose=True ):
105 for c in clients:
106 out, err = c.communicate()
107 if verbose:
108 if err:
109 info( err )
110 else:
111 bw = out.split( ',' )[8]
112 info( '%s <--> %s: %s\n' % ( c.s, c.d, formatBw(bw) ) )
113 for s in servers:
114 s.terminate()
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800115
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800116 try:
117 info ( 'Press ^Z to continue in background or ^C to abort\n')
118 progress( seconds )
119 finish()
120 except KeyboardInterrupt:
121 for c in clients:
122 c.terminate()
123 for s in servers:
124 s.terminate()
125 except BackgroundException:
126 info( '\n*** Continuing in background...\n' )
127 t = Thread( target=finish, args=[ False ] )
128 t.start()
129 finally:
130 #Disable custom background signal
131 signal.signal(signal.SIGTSTP, oldSignal)
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800132
133def progress(t):
134 while t > 0:
135 sys.stdout.write( '.' )
136 t -= 1
137 sys.stdout.flush()
138 sleep(1)
139 print
140
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800141def formatBw( bw ):
142 bw = float(bw)
143 if bw > 1000:
144 bw /= 1000
145 if bw > 1000:
146 bw /= 1000
147 if bw > 1000:
148 bw /= 1000
149 return '%.2f Gbps' % bw
150 return '%.2f Mbps' % bw
151 return '%.2f Kbps' % bw
152 return '%.2f bps' % bw
Thomas Vachuska9ee49792016-03-01 16:51:14 -0800153
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800154class BackgroundException( Exception ):
155 pass
156
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200157
158def get_mn(mn):
159 if isinstance(mn, ONOSMininet):
160 return mn
161 elif isinstance(mn, MininetFacade):
162 # There's more Mininet objects instantiated (e.g. one for the control network in onos.py).
163 for net in mn.nets:
164 if isinstance(net, ONOSMininet):
165 return net
166 return None
167
168
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800169def do_bgIperf( self, line ):
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800170 args = line.split()
171 if not args:
172 output( 'Provide a list of hosts.\n' )
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800173
174 #Try to parse the '-t' argument as the number of seconds
175 seconds = 10
176 for i, arg in enumerate(args):
177 if arg == '-t':
178 if i + 1 < len(args):
179 try:
180 seconds = int(args[i + 1])
181 except ValueError:
182 error( 'Could not parse number of seconds: %s', args[i+1] )
183 del(args[i+1])
184 del args[i]
185
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800186 hosts = []
187 err = False
188 for arg in args:
189 if arg not in self.mn:
190 err = True
191 error( "node '%s' not in network\n" % arg )
192 else:
193 hosts.append( self.mn[ arg ] )
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200194 mn = get_mn( self.mn )
195 if "bgIperf" in dir( mn ) and not err:
196 mn.bgIperf( hosts, seconds=seconds )
197 else:
198 output('Background Iperf is not supported.\n')
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800199
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800200def do_gratuitousArp( self, line ):
201 args = line.split()
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200202 mn = get_mn(self.mn)
203 if "gratuitousArp" in dir( mn ):
204 mn.gratuitousArp( args )
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800205 else:
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800206 output( 'Gratuitous ARP is not supported.\n' )
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800207
Brian O'Connore9b4dd72016-03-05 01:07:18 -0800208CLI.do_bgIperf = do_bgIperf
Brian O'Connord6c73fb2016-03-04 15:24:52 -0800209CLI.do_gratuitousArp = do_gratuitousArp
210
Luca Prete3d402dc2016-12-13 16:25:03 -0800211def parse_args():
212 parser = ArgumentParser(description='ONOS Mininet')
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200213 parser.add_argument('--cluster-size', help='Starts an ONOS cluster with the given number of instances',
214 type=int, action='store', dest='clusterSize', required=False, default=0)
215 parser.add_argument('--netcfg', help='Relative path of the JSON file to be used with netcfg',
216 type=str, action='store', dest='netcfgJson', required=False, default='')
217 parser.add_argument('ipAddrs', metavar='IP', type=str, nargs='*',
218 help='List of controller IP addresses', default=[])
Luca Prete3d402dc2016-12-13 16:25:03 -0800219 return parser.parse_args()
220
221def run( topo, controllers=None, link=TCLink, autoSetMacs=True):
222 if not topo:
223 print 'Need to provide a topology'
224 exit(1)
225
226 args = parse_args()
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200227
228 if not controllers and len(args.ipAddrs) > 0:
229 controllers = args.ipAddrs
230
231 if not controllers and args.clusterSize < 1:
232 print 'Need to provide a list of controller IPs, or define a cluster size.'
Thomas Vachuska9ee49792016-03-01 16:51:14 -0800233 exit( 1 )
234
235 setLogLevel( 'info' )
236
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200237 if args.clusterSize > 0:
238 if 'ONOS_ROOT' not in os.environ:
239 print "Environment var $ONOS_ROOT not set (needed to import onos.py)"
240 exit( 1 )
241 sys.path.append(os.environ["ONOS_ROOT"] + "/tools/dev/mininet")
242 from onos import ONOSCluster, ONOSOVSSwitch, ONOSCLI
243 controller = ONOSCluster('c0', args.clusterSize)
244 onosAddr = controller.nodes()[0].IP()
245 net = ONOSMininet( topo=topo, controller=controller, switch=ONOSOVSSwitch, link=link,
246 autoSetMacs=autoSetMacs )
247 cli = ONOSCLI
248 else:
249 onosAddr = controllers[0]
250 net = ONOSMininet(topo=topo, controllers=controllers, link=link, autoSetMacs=autoSetMacs)
251 cli = CLI
252
Thomas Vachuska9ee49792016-03-01 16:51:14 -0800253 net.start()
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200254
255 if len(args.netcfgJson) > 0:
256 if not os.path.isfile(args.netcfgJson):
257 error('*** WARNING no such netcfg file: %s\n' % args.netcfgJson)
258 else:
259 info('*** Setting netcfg: %s\n' % args.netcfgJson)
260 call(("onos-netcfg", onosAddr, args.netcfgJson))
261
262 cli( net )
Thomas Vachuska9ee49792016-03-01 16:51:14 -0800263 net.stop()