blob: a1550fc0016737fe179759cf248cd46a8a62079b [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
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200211def run( topo, controllers=None, link=TCLink, autoSetMacs=True):
212 if not topo:
213 print 'Need to provide a topology'
214 exit(1)
215
216 parser = ArgumentParser(description='ONOS Mininet ' + type(topo).__name__)
217 parser.add_argument('--cluster-size', help='Starts an ONOS cluster with the given number of instances',
218 type=int, action='store', dest='clusterSize', required=False, default=0)
219 parser.add_argument('--netcfg', help='Relative path of the JSON file to be used with netcfg',
220 type=str, action='store', dest='netcfgJson', required=False, default='')
221 parser.add_argument('ipAddrs', metavar='IP', type=str, nargs='*',
222 help='List of controller IP addresses', default=[])
223 args = parser.parse_args()
224
225 if not controllers and len(args.ipAddrs) > 0:
226 controllers = args.ipAddrs
227
228 if not controllers and args.clusterSize < 1:
229 print 'Need to provide a list of controller IPs, or define a cluster size.'
Thomas Vachuska9ee49792016-03-01 16:51:14 -0800230 exit( 1 )
231
232 setLogLevel( 'info' )
233
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200234 if args.clusterSize > 0:
235 if 'ONOS_ROOT' not in os.environ:
236 print "Environment var $ONOS_ROOT not set (needed to import onos.py)"
237 exit( 1 )
238 sys.path.append(os.environ["ONOS_ROOT"] + "/tools/dev/mininet")
239 from onos import ONOSCluster, ONOSOVSSwitch, ONOSCLI
240 controller = ONOSCluster('c0', args.clusterSize)
241 onosAddr = controller.nodes()[0].IP()
242 net = ONOSMininet( topo=topo, controller=controller, switch=ONOSOVSSwitch, link=link,
243 autoSetMacs=autoSetMacs )
244 cli = ONOSCLI
245 else:
246 onosAddr = controllers[0]
247 net = ONOSMininet(topo=topo, controllers=controllers, link=link, autoSetMacs=autoSetMacs)
248 cli = CLI
249
Thomas Vachuska9ee49792016-03-01 16:51:14 -0800250 net.start()
Carmelo Casconec52a4b12016-10-24 16:47:17 +0200251
252 if len(args.netcfgJson) > 0:
253 if not os.path.isfile(args.netcfgJson):
254 error('*** WARNING no such netcfg file: %s\n' % args.netcfgJson)
255 else:
256 info('*** Setting netcfg: %s\n' % args.netcfgJson)
257 call(("onos-netcfg", onosAddr, args.netcfgJson))
258
259 cli( net )
Thomas Vachuska9ee49792016-03-01 16:51:14 -0800260 net.stop()