blob: 1cff74c09ca4efc07b1aa8dcbbdca28a4c1c0647 [file] [log] [blame]
Bob Lantzc7c05402013-12-16 21:22:12 -08001#!/usr/bin/env python
2
3"""
Bob Lantz7ead0532013-12-17 20:41:20 -08004onos.py: A basic (?) ONOS Controller() subclass for Mininet
Bob Lantzc7c05402013-12-16 21:22:12 -08005
6We implement the following classes:
7
8ONOSController: a custom Controller() subclass to start ONOS
9OVSSwitchONOS: a custom OVSSwitch() switch that connects to multiple controllers.
10
11We use single Zookeeper and Cassandra instances for now.
12
13As a custom file, exports:
14
15--controller onos
16--switch ovso
17
18Usage:
19
20$ sudo ./onos.py
21
22This will start up a simple 2-host, 2 ONOS network
23
24$ sudo mn --custom onos.py --controller onos,2 --switch ovso
25"""
26
27from mininet.node import Controller, OVSSwitch
28from mininet.net import Mininet
29from mininet.cli import CLI
Bob Lantz7ead0532013-12-17 20:41:20 -080030from mininet.topo import LinearTopo
31from mininet.log import setLogLevel, info, warn
Bob Lantzc7c05402013-12-16 21:22:12 -080032from mininet.util import quietRun
33
34from shutil import copyfile
35from os import environ
36from functools import partial
Bob Lantz7ead0532013-12-17 20:41:20 -080037from time import time, sleep
38from sys import argv
Bob Lantzc7c05402013-12-16 21:22:12 -080039
40class ONOS( Controller ):
41 "Custom controller class for ONOS"
42
43 # Directories and configuration templates
44 home = environ[ 'HOME' ]
45 onosDir = home + "/ONOS"
46 zookeeperDir = home + "/zookeeper-3.4.5"
47 dirBase = '/tmp'
48 logDir = dirBase + '/onos-%s.logs'
49 cassDir = dirBase + '/onos-%s.cassandra'
50 configFile = dirBase + '/onos-%s.properties'
51 logbackFile = dirBase + '/onos-%s.logback'
Bob Lantz7ead0532013-12-17 20:41:20 -080052
53 # Base ONOS modules
54 baseModules = (
55 'net.floodlightcontroller.core.FloodlightProvider',
56 'net.floodlightcontroller.threadpool.ThreadPool',
57 'net.onrc.onos.ofcontroller.floodlightlistener.NetworkGraphPublisher',
58 'net.floodlightcontroller.ui.web.StaticWebRoutable',
59 'net.onrc.onos.datagrid.HazelcastDatagrid',
60 'net.onrc.onos.ofcontroller.flowmanager.FlowManager',
61 'net.onrc.onos.ofcontroller.flowprogrammer.FlowProgrammer',
62 'net.onrc.onos.ofcontroller.topology.TopologyManager',
63 'net.onrc.onos.registry.controller.ZookeeperRegistry'
64 )
65
66 # Additions for reactive forwarding
67 reactiveModules = (
68 'net.onrc.onos.ofcontroller.proxyarp.ProxyArpManager',
69 'net.onrc.onos.ofcontroller.core.config.DefaultConfiguration',
70 'net.onrc.onos.ofcontroller.forwarding.Forwarding'
71 )
72
73 # Module parameters
Bob Lantzc7c05402013-12-16 21:22:12 -080074 ofbase = 6633
Bob Lantz7ead0532013-12-17 20:41:20 -080075 restbase = 8080
76 jmxbase = 7189
Bob Lantzc7c05402013-12-16 21:22:12 -080077
Bob Lantzc7c05402013-12-16 21:22:12 -080078 fc = 'net.floodlightcontroller.'
Bob Lantzc7c05402013-12-16 21:22:12 -080079
Bob Lantz7ead0532013-12-17 20:41:20 -080080 perNodeConfigBase = {
81 fc + 'core.FloodlightProvider.openflowport': ofbase,
82 fc + 'restserver.RestApiServer.port': restbase,
83 fc + 'core.FloodlightProvider.controllerid': 0
84 }
85
86 staticConfig = {
87 'net.onrc.onos.ofcontroller.floodlightlistener.NetworkGraphPublisher.dbconf':
88 '/tmp/cassandra.titan',
89 'net.onrc.onos.datagrid.HazelcastDatagrid.datagridConfig':
90 onosDir + '/conf/hazelcast.xml',
91 'net.floodlightcontroller.core.FloodlightProvider.workerthreads': 16,
92 'net.floodlightcontroller.forwarding.Forwarding.idletimeout': 5,
93 'net.floodlightcontroller.forwarding.Forwarding.hardtimeout': 0
94 }
95
96 proctag = 'mn-onos-id'
Bob Lantzc7c05402013-12-16 21:22:12 -080097
Bob Lantz7a4d6102013-12-17 00:34:55 -080098 # For maven debugging
99 # mvn = 'mvn -o -e -X'
Bob Lantzc7c05402013-12-16 21:22:12 -0800100
Bob Lantz7ead0532013-12-17 20:41:20 -0800101 def __init__( self, name, n=1, reactive=True, runAsRoot=False, **params):
Bob Lantzc7c05402013-12-16 21:22:12 -0800102 """n: number of ONOS instances to run (1)
Bob Lantz7ead0532013-12-17 20:41:20 -0800103 reactive: run in reactive mode (True)
104 runAsRoot: run ONOS as root (False)"""
Bob Lantzc7c05402013-12-16 21:22:12 -0800105 self.check()
Bob Lantzc7c05402013-12-16 21:22:12 -0800106 self.count = n
Bob Lantz7ead0532013-12-17 20:41:20 -0800107 self.reactive = reactive
108 self.runAsRoot = runAsRoot
Bob Lantzc7c05402013-12-16 21:22:12 -0800109 self.ids = range( 0, self.count )
110 Controller.__init__( self, name, **params )
111 # We don't need to run as root, and it can interfere
112 # with starting Zookeeper manually
Bob Lantz7ead0532013-12-17 20:41:20 -0800113 self.user = None
114 if not self.runAsRoot:
115 try:
116 self.user = quietRun( 'who am i' ).split()[ 0 ]
117 self.sendCmd( 'su', self.user )
118 self.waiting = False
119 except:
120 warn( '__init__: failed to drop privileges\n' )
Bob Lantzc7c05402013-12-16 21:22:12 -0800121 # Need to run commands from ONOS dir
122 self.cmd( 'cd', self.onosDir )
123 self.cmd( 'export PATH=$PATH:%s' % self.onosDir )
Bob Lantz7a4d6102013-12-17 00:34:55 -0800124 if hasattr( self, 'mvn' ):
125 self.cmd( 'export MVN="%s"' % self.mvn )
Bob Lantzc7c05402013-12-16 21:22:12 -0800126
127 def check( self ):
Bob Lantz7ead0532013-12-17 20:41:20 -0800128 "Check for ONOS prerequisites"
Bob Lantzc7c05402013-12-16 21:22:12 -0800129 if not quietRun( 'which java' ):
130 raise Exception( 'java not found -'
131 ' make sure it is installed and in $PATH' )
132 if not quietRun( 'which mvn' ):
133 raise Exception( 'Maven (mvn) not found -'
134 ' make sure it is installed and in $PATH' )
135
Bob Lantz7ead0532013-12-17 20:41:20 -0800136
137 def waitNetstat( self, pid ):
138 """Wait for pid to show up in netstat
139 We assume that once a process is listening on some
140 port, it is ready to go!"""
141 while True:
142 output = self.cmd( 'sudo netstat -natp | grep %s/' % pid )
143 if output:
144 return output
145 info( '.' )
146 sleep( 1 )
147
148 def waitStart( self, procname, pattern ):
149 "Wait for at least one of procname to show up in netstat"
150 info( '* Waiting for %s startup' % procname )
151 result = self.cmd( 'pgrep -f %s' % pattern ).split()[ 0 ]
152 pid = int( result )
153 output = self.waitNetstat( pid )
154 info( '\n* %s process %d is listening\n' % ( procname, pid ) )
155 info( output )
156
Bob Lantzc7c05402013-12-16 21:22:12 -0800157 def startCassandra( self ):
158 "Start Cassandra"
159 self.cmd( 'start-cassandra.sh start' )
Bob Lantz7ead0532013-12-17 20:41:20 -0800160 self.waitStart( 'Cassandra', 'apache-cassandra' )
Bob Lantzc7c05402013-12-16 21:22:12 -0800161 status = self.cmd( 'start-cassandra.sh status' )
Bob Lantz7ead0532013-12-17 20:41:20 -0800162 if 'running' not in status:
Bob Lantzc7c05402013-12-16 21:22:12 -0800163 raise Exception( 'Cassandra startup failed: ' + status )
164
165 def stopCassandra( self ):
166 "Stop Cassandra"
167 self.cmd( 'start-cassandra.sh stop' )
168
169 def startZookeeper( self, initcfg=True ):
170 "Start Zookeeper"
171 # Reinitialize configuration file
172 if initcfg:
173 cfg = self.zookeeperDir + '/conf/zoo.cfg'
174 template = self.zookeeperDir + '/conf/zoo_sample.cfg'
175 copyfile( template, cfg )
176 self.cmd( 'start-zk.sh restart' )
Bob Lantz7ead0532013-12-17 20:41:20 -0800177 self.waitStart( 'Zookeeper', 'zookeeper' )
Bob Lantzc7c05402013-12-16 21:22:12 -0800178 status = self.cmd( 'start-zk.sh status' )
179 if 'Error' in status:
180 raise Exception( 'Zookeeper startup failed: ' + status )
181
182 def stopZookeeper( self ):
183 "Stop Zookeeper"
184 self.cmd( 'start-zk.sh stop' )
185
Bob Lantz7ead0532013-12-17 20:41:20 -0800186 def genProperties( self, id, path='/tmp' ):
187 "Generate ONOS properties file"
188 filename = path + '/onos-%s.properties' % id
189 with open( filename, 'w' ) as f:
190 # Write modules list
191 modules = list( self.baseModules )
192 if self.reactive:
193 modules += list( self.reactiveModules )
194 f.write( 'floodlight.modules = %s\n' %
195 ',\\\n'.join( modules ) )
196 # Write other parameters
197 for var, val in self.perNodeConfigBase.iteritems():
198 if type( val ) is int:
199 val += id
200 f.write( '%s = %s\n' % ( var, val ) )
201 for var, val in self.staticConfig.iteritems():
202 f.write( '%s = %s\n' % ( var, val ) )
203 return filename
204
205 def setVars( self, id, propsFile ):
206 """Set and return environment vars
207 id: ONOS instance number
208 propsFile: properties file name"""
Bob Lantzc7c05402013-12-16 21:22:12 -0800209 # ONOS directories and files
210 logdir = self.logDir % id
211 cassdir = self.cassDir % id
212 logback = self.logbackFile % id
213 jmxport = self.jmxbase + id
214 self.cmd( 'mkdir -p', logdir, cassdir )
215 self.cmd( 'export ONOS_LOGDIR="%s"' % logdir )
216 self.cmd( 'export ZOO_LOG_DIR="%s"' % logdir )
217 self.cmd( 'export CASS_DIR="%s"' % cassdir )
218 self.cmd( 'export ONOS_LOGBACK="%s"' % logback )
219 self.cmd( 'export JMX_PORT=%s' % jmxport )
Bob Lantz7ead0532013-12-17 20:41:20 -0800220 self.cmd( 'export JVM_OPTS="-D%s=%s"' % (
221 self.proctag, id ) )
222 self.cmd( 'export ONOS_PROPS="%s"' % propsFile )
Bob Lantzc7c05402013-12-16 21:22:12 -0800223
224 def startONOS( self, id ):
225 """Start ONOS
Bob Lantz7ead0532013-12-17 20:41:20 -0800226 id: new instance number"""
227 start = time()
228 self.stopONOS( id )
229 propsFile = self.genProperties( id )
230 self.setVars( id, propsFile )
Bob Lantzc7c05402013-12-16 21:22:12 -0800231 self.cmdPrint( 'start-onos.sh startnokill' )
Bob Lantz7ead0532013-12-17 20:41:20 -0800232 # start-onos.sh waits for ONOS startup
233 elapsed = time() - start
234 info( '* ONOS %s started in %.2f seconds\n' % ( id, elapsed ) )
Bob Lantzc7c05402013-12-16 21:22:12 -0800235
236 def stopONOS( self, id ):
237 """Shut down ONOS
238 id: identifier for instance"""
239 pid = self.cmd( "jps -v | grep %s=%s | awk '{print $1}'" %
240 ( self.proctag, id ) ).strip()
241 if pid:
242 self.cmdPrint( 'kill', pid )
243
244 def start( self, *args ):
245 "Start ONOS instances"
246 info( '* Starting Cassandra\n' )
247 self.startCassandra()
248 info( '* Starting Zookeeper\n' )
249 self.startZookeeper()
250 for id in self.ids:
251 info( '* Starting ONOS %s\n' % id )
252 self.startONOS( id )
253
254 def stop( self, *args ):
255 "Stop ONOS instances"
256 for id in self.ids:
257 info( '* Stopping ONOS %s\n' % id )
258 self.stopONOS( id )
Bob Lantz7ead0532013-12-17 20:41:20 -0800259 info( '* Stopping Zookeeper\n' )
Bob Lantzc7c05402013-12-16 21:22:12 -0800260 self.stopZookeeper()
261 info( '* Stopping Cassandra\n' )
262 self.stopCassandra()
263
264 def clist( self ):
265 "Return list of controller specifiers (proto:ip:port)"
266 return [ 'tcp:127.0.0.1:%s' % ( self.ofbase + id )
267 for id in range( 0, self.count ) ]
268
269
270class OVSSwitchONOS( OVSSwitch ):
271 "OVS switch which connects to multiple controllers"
272 def start( self, controllers ):
273 OVSSwitch.start( self, controllers )
274 assert len( controllers ) == 1
275 c0 = controllers[ 0 ]
276 assert type( c0 ) == ONOS
277 clist = ','.join( c0.clist() )
278 self.cmd( 'ovs-vsctl set-controller', self, clist)
279 # Reconnect quickly to controllers (1s vs. 15s max_backoff)
280 for uuid in self.controllerUUIDs():
281 if uuid.count( '-' ) != 4:
282 # Doesn't look like a UUID
283 continue
284 uuid = uuid.strip()
285 self.cmd( 'ovs-vsctl set Controller', uuid,
286 'max_backoff=1000' )
287
288
Bob Lantz7ead0532013-12-17 20:41:20 -0800289def waitConnected( switches ):
290 "Wait until all switches connect to controllers"
291 start = time()
292 info( '* Waiting for switches to connect...\n' )
293 for s in switches:
294 info( s )
295 while not s.connected():
296 info( '.' )
297 sleep( 1 )
298 info( ' ' )
299 elapsed = time() - start
300 info( '\n* Connected in %.2f seconds\n' % elapsed )
301
302
Bob Lantzc7c05402013-12-16 21:22:12 -0800303controllers = { 'onos': ONOS }
304switches = { 'ovso': OVSSwitchONOS }
305
306
307if __name__ == '__main__':
Bob Lantz7ead0532013-12-17 20:41:20 -0800308 # Simple test for ONOS() controller class
Bob Lantzc7c05402013-12-16 21:22:12 -0800309 setLogLevel( 'info' )
Bob Lantz7ead0532013-12-17 20:41:20 -0800310 size = 2 if len( argv ) != 2 else int( argv[ 1 ] )
311 net = Mininet( topo=LinearTopo( size ),
Bob Lantzc7c05402013-12-16 21:22:12 -0800312 controller=partial( ONOS, n=2 ),
313 switch=OVSSwitchONOS )
314 net.start()
Bob Lantz7ead0532013-12-17 20:41:20 -0800315 waitConnected( net.switches )
Bob Lantzc7c05402013-12-16 21:22:12 -0800316 CLI( net )
317 net.stop()