blob: 25f0ebf571b78c9e6d93e66efc0fd43296e40f77 [file] [log] [blame]
Bob Lantzc7c05402013-12-16 21:22:12 -08001#!/usr/bin/env python
2
3"""
4onos.py: A simple ONOS Controller() subclass for Mininet
5
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
30from mininet.topo import SingleSwitchTopo
31from mininet.log import setLogLevel, info
32from mininet.util import quietRun
33
34from shutil import copyfile
35from os import environ
36from functools import partial
37
38
39class ONOS( Controller ):
40 "Custom controller class for ONOS"
41
42 # Directories and configuration templates
43 home = environ[ 'HOME' ]
44 onosDir = home + "/ONOS"
45 zookeeperDir = home + "/zookeeper-3.4.5"
46 dirBase = '/tmp'
47 logDir = dirBase + '/onos-%s.logs'
48 cassDir = dirBase + '/onos-%s.cassandra'
49 configFile = dirBase + '/onos-%s.properties'
50 logbackFile = dirBase + '/onos-%s.logback'
51 jmxbase = 7189
52 restbase = 8080
53 ofbase = 6633
54
55 # Per-instance property template
56 fc = 'net.floodlightcontroller.'
57 proctag = 'mn-onos-id'
58 jvmopts = (
59 # We match on this to shut down our instances
60 ( proctag, 0 ),
61 ( fc + 'restserver.RestApiServer.port', restbase ),
62 ( fc + 'core.FloodlightProvider.openflowport', ofbase ),
63 ( fc + 'core.FloodlightProvider.controllerid', 0 ) )
64
65
66 # Make mvn not suck
67 mvn = 'mvn -e -X'
68
69 def __init__( self, name, n=1, drop=True, **params):
70 """n: number of ONOS instances to run (1)
71 drop: drop root privileges (True)"""
72 self.check()
73 self.drop = drop
74 self.count = n
75 self.ids = range( 0, self.count )
76 Controller.__init__( self, name, **params )
77 # We don't need to run as root, and it can interfere
78 # with starting Zookeeper manually
79 if self.drop:
80 self.user = quietRun( 'who am i' ).split()[ 0 ]
81 self.sendCmd( 'su', self.user )
82 self.waiting = False
83 # Need to run commands from ONOS dir
84 self.cmd( 'cd', self.onosDir )
85 self.cmd( 'export PATH=$PATH:%s' % self.onosDir )
86 self.cmd( 'export MVN="%s"' % self.mvn )
87
88 def check( self ):
89 "Check for prerequisites"
90 if not quietRun( 'which java' ):
91 raise Exception( 'java not found -'
92 ' make sure it is installed and in $PATH' )
93 if not quietRun( 'which mvn' ):
94 raise Exception( 'Maven (mvn) not found -'
95 ' make sure it is installed and in $PATH' )
96
97 def startCassandra( self ):
98 "Start Cassandra"
99 self.cmd( 'start-cassandra.sh start' )
100 status = self.cmd( 'start-cassandra.sh status' )
101 if 'Error' in status:
102 raise Exception( 'Cassandra startup failed: ' + status )
103
104 def stopCassandra( self ):
105 "Stop Cassandra"
106 self.cmd( 'start-cassandra.sh stop' )
107
108 def startZookeeper( self, initcfg=True ):
109 "Start Zookeeper"
110 # Reinitialize configuration file
111 if initcfg:
112 cfg = self.zookeeperDir + '/conf/zoo.cfg'
113 template = self.zookeeperDir + '/conf/zoo_sample.cfg'
114 copyfile( template, cfg )
115 self.cmd( 'start-zk.sh restart' )
116 status = self.cmd( 'start-zk.sh status' )
117 if 'Error' in status:
118 raise Exception( 'Zookeeper startup failed: ' + status )
119
120 def stopZookeeper( self ):
121 "Stop Zookeeper"
122 self.cmd( 'start-zk.sh stop' )
123
124 def setVars( self, id ):
125 "Set and return environment vars"
126 # ONOS directories and files
127 logdir = self.logDir % id
128 cassdir = self.cassDir % id
129 logback = self.logbackFile % id
130 jmxport = self.jmxbase + id
131 self.cmd( 'mkdir -p', logdir, cassdir )
132 self.cmd( 'export ONOS_LOGDIR="%s"' % logdir )
133 self.cmd( 'export ZOO_LOG_DIR="%s"' % logdir )
134 self.cmd( 'export CASS_DIR="%s"' % cassdir )
135 self.cmd( 'export ONOS_LOGBACK="%s"' % logback )
136 self.cmd( 'export JMX_PORT=%s' % jmxport )
137 jvmopts = ('-agentlib:jdwp=transport=dt_socket,address=%s,server=y,suspend=n '
138 % ( 8000 + id ) )
139 jvmopts += ' '.join( '-D%s=%s '% ( opt, val + id )
140 for opt, val in self.jvmopts )
141 self.cmd( 'export JVM_OPTS="%s"' % jvmopts )
142
143 def startONOS( self, id ):
144 """Start ONOS
145 id: identifier for new instance"""
146 # self.stopONOS( id )
147 self.setVars( id )
148 self.cmdPrint( 'start-onos.sh startnokill' )
149
150 def stopONOS( self, id ):
151 """Shut down ONOS
152 id: identifier for instance"""
153 pid = self.cmd( "jps -v | grep %s=%s | awk '{print $1}'" %
154 ( self.proctag, id ) ).strip()
155 if pid:
156 self.cmdPrint( 'kill', pid )
157
158 def start( self, *args ):
159 "Start ONOS instances"
160 info( '* Starting Cassandra\n' )
161 self.startCassandra()
162 info( '* Starting Zookeeper\n' )
163 self.startZookeeper()
164 for id in self.ids:
165 info( '* Starting ONOS %s\n' % id )
166 self.startONOS( id )
167
168 def stop( self, *args ):
169 "Stop ONOS instances"
170 for id in self.ids:
171 info( '* Stopping ONOS %s\n' % id )
172 self.stopONOS( id )
173 info( '* Stopping zookeeper\n' )
174 self.stopZookeeper()
175 info( '* Stopping Cassandra\n' )
176 self.stopCassandra()
177
178 def clist( self ):
179 "Return list of controller specifiers (proto:ip:port)"
180 return [ 'tcp:127.0.0.1:%s' % ( self.ofbase + id )
181 for id in range( 0, self.count ) ]
182
183
184class OVSSwitchONOS( OVSSwitch ):
185 "OVS switch which connects to multiple controllers"
186 def start( self, controllers ):
187 OVSSwitch.start( self, controllers )
188 assert len( controllers ) == 1
189 c0 = controllers[ 0 ]
190 assert type( c0 ) == ONOS
191 clist = ','.join( c0.clist() )
192 self.cmd( 'ovs-vsctl set-controller', self, clist)
193 # Reconnect quickly to controllers (1s vs. 15s max_backoff)
194 for uuid in self.controllerUUIDs():
195 if uuid.count( '-' ) != 4:
196 # Doesn't look like a UUID
197 continue
198 uuid = uuid.strip()
199 self.cmd( 'ovs-vsctl set Controller', uuid,
200 'max_backoff=1000' )
201
202
203controllers = { 'onos': ONOS }
204switches = { 'ovso': OVSSwitchONOS }
205
206
207if __name__ == '__main__':
208 "Simple test of ONOSController"
209 setLogLevel( 'info' )
210 net = Mininet( topo=SingleSwitchTopo( 2 ),
211 controller=partial( ONOS, n=2 ),
212 switch=OVSSwitchONOS )
213 net.start()
214 CLI( net )
215 net.stop()