blob: 6dca835b6477e9474972ae7e8b4b6d75f476c64e [file] [log] [blame]
Brian O'Connor195191b2014-10-22 01:09:36 -07001#!/usr/bin/env python
2
3# TODO add onos-app-fwd to features
4# TODO check if service is running... i think this might already be done by mn
5
6from mininet.node import Controller, OVSSwitch, CPULimitedHost, RemoteController
7from mininet.net import Mininet
8from mininet.cli import CLI
9from mininet.topo import LinearTopo, Topo
10from mininet.log import setLogLevel, info, warn
11from mininet.util import quietRun, numCores
12
13from shutil import copyfile
14from os import environ, path
15from functools import partial
16import time
17from sys import argv
18from time import sleep
Brian O'Connor67eb3802014-10-22 19:55:38 -070019from sets import Set
Brian O'Connor195191b2014-10-22 01:09:36 -070020
21class ONOS( Controller ):
Brian O'Connor67eb3802014-10-22 19:55:38 -070022 "TODO"
23
24 onosDir = '/opt/onos/'
25
26 def __init__( self, name, onosDir=onosDir,
27 reactive=True, features=[ 'onos-app-tvue' ],
28 **kwargs ):
29 '''TODO'''
30
31 Controller.__init__( self, name, **kwargs )
32 # the following have been done for us:
33 #self.ip = ip ('127.0.0.1')
34 #self.port = port (6633)
35 #self.protocol = protocol ('tcp')
36 #self.checkListening()
37
38 self.onosDir = onosDir
Pavlin Radoslavovc5227052014-11-04 10:55:19 -080039 self.karafDir = onosDir + 'apache-karaf-3.0.2/'
Brian O'Connor67eb3802014-10-22 19:55:38 -070040 self.instanceDir = self.karafDir
41
42 # add default modules
43 # TODO: consider an ordered set
44 self.features = Set([ 'webconsole',
Jonathan Hart7b1f5cb2014-11-11 16:05:29 -080045 'onos-rest',
Brian O'Connor67eb3802014-10-22 19:55:38 -070046 'onos-api',
47 'onos-cli',
48 'onos-openflow' ])
49 self.features.update( features )
50 # add reactive forwarding modules
51 if reactive:
52 self.features.update( ['onos-app-fwd',
53 'onos-app-proxyarp',
54 'onos-app-mobility' ] )
55 # add the distributed core if we are in a namespace with no trivial core
56 if self.inNamespace and 'onos-core-trivial' not in self.features:
57 self.features.add( 'onos-core' )
58 # if there is no core, add the trivial one
59 if 'onos-core' not in self.features:
60 self.features.add( 'onos-core-trivial' )
61 print self.features
Brian O'Connor195191b2014-10-22 01:09:36 -070062
63 def start( self ):
Brian O'Connor195191b2014-10-22 01:09:36 -070064 if self.inNamespace:
Brian O'Connor67eb3802014-10-22 19:55:38 -070065 instanceOpts = ( '-furl mvn:org.onlab.onos/onos-features/1.0.0-SNAPSHOT/xml/features '
66 '-s 8101' )
Jonathan Hart7b1f5cb2014-11-11 16:05:29 -080067 if self.ip is not None:
68 instanceOpts += (' -a %s' % self.IP() )
Brian O'Connor67eb3802014-10-22 19:55:38 -070069 self.userCmd( self.karafDir + 'bin/instance create %s %s' % ( instanceOpts, self.name ) )
70 self.instanceDir = self.karafDir + 'instances/%s/' % self.name
Brian O'Connor195191b2014-10-22 01:09:36 -070071 else:
72 # we are running in the root namespace, so let's use the root instance
Brian O'Connor67eb3802014-10-22 19:55:38 -070073 # clean up the data directory
74 #self.userCmd( 'rm -rf '+ self.karafDir + 'data/' )
75 pass
Brian O'Connor195191b2014-10-22 01:09:36 -070076
Brian O'Connor67eb3802014-10-22 19:55:38 -070077 self.userCmd( 'rm -rf '+ self.instanceDir + 'data/' )
78
79 # Update etc/org.apache.karaf.features.cfg
80 self.updateFeatures()
81
82 # TODO 2. Update etc/hazelcast.xml : interface lines
83 #cp etc/hazelcast.xml instances/c1/etc/
84 self.updateHazelcast()
85
86 # TODO 3. Update etc/system.properties : onos.ip
87 # TODO 4. Update config/cluster.json : with all nodes
88
89 # start onos
90 self.userCmd( self.instanceDir + 'bin/start' )
Brian O'Connor195191b2014-10-22 01:09:36 -070091 #TODO we should wait for startup...
92
93 def stop( self ):
Brian O'Connor67eb3802014-10-22 19:55:38 -070094 self.userCmd( self.instanceDir + 'bin/stop' )
95 #if self.inNamespace:
96 # self.userCmd( self.karafDir + 'bin/instance destroy %s' % self.name )
Brian O'Connor195191b2014-10-22 01:09:36 -070097 self.terminate()
98
Brian O'Connor67eb3802014-10-22 19:55:38 -070099 def updateHazelcast( self ):
100 readfile = self.karafDir + 'etc/hazelcast.xml'
101 writefile = self.instanceDir + 'etc/hazelcast.xml'
102 with open( readfile, 'r' ) as r:
103 with open( writefile, 'w' ) as w:
104 for line in r.readlines():
105 if '<interface>' in line:
106 line = '<interface>' + '192.168.123.*' + '</interface>\n'
107 w.write( line )
108
109 def updateFeatures( self ):
110 filename = self.instanceDir + 'etc/org.apache.karaf.features.cfg'
Brian O'Connor195191b2014-10-22 01:09:36 -0700111 with open( filename, 'r+' ) as f:
112 lines = f.readlines()
113 f.seek(0)
114 f.truncate()
115 for line in lines:
116 #print '?', line,
117 if 'featuresBoot=' in line:
Brian O'Connor67eb3802014-10-22 19:55:38 -0700118 # parse the features from the line
119 features = line.rstrip().split('=')[1].split(',')
120 # add the features to our features set
121 self.features.update( features )
122 # generate the new features line
123 line = 'featuresBoot=' + ','.join( self.features ) + '\n'
Brian O'Connor195191b2014-10-22 01:09:36 -0700124 #print '!', line,
125 f.write( line )
126
Brian O'Connor67eb3802014-10-22 19:55:38 -0700127
Brian O'Connor195191b2014-10-22 01:09:36 -0700128 @classmethod
129 def isAvailable( self ):
Brian O'Connor67eb3802014-10-22 19:55:38 -0700130 return quietRun( 'ls %s' % self.onosDir )
131
132 def userCmd( self, cmd ):
133 # switch to the non-root user because karaf gets upset otherwise
134 # because the .m2repo is not stored with root
135 cmd = 'sudo -u %s %s' % ( self.findUser(), cmd )
136 return self.cmd( cmd )
Brian O'Connor195191b2014-10-22 01:09:36 -0700137
138 @staticmethod
139 def findUser():
140 "Try to return logged-in (usually non-root) user"
141 try:
142 # If we're running sudo
143 return os.environ[ 'SUDO_USER' ]
144 except:
145 try:
146 # Logged-in user (if we have a tty)
147 return quietRun( 'who am i' ).split()[ 0 ]
148 except:
149 # Give up and return effective user
150 return quietRun( 'whoami' )
151
152
153class ControlNetwork( Topo ):
154 "Control Network Topology"
155 def __init__( self, n, dataController=ONOS, **kwargs ):
156 """n: number of data network controller nodes
157 dataController: class for data network controllers"""
158 Topo.__init__( self, **kwargs )
159 # Connect everything to a single switch
160 cs0 = self.addSwitch( 'cs0' )
161 # Add hosts which will serve as data network controllers
Brian O'Connor67eb3802014-10-22 19:55:38 -0700162 for i in range( 1, n+1 ):
Brian O'Connor195191b2014-10-22 01:09:36 -0700163 c = self.addHost( 'c%s' % i, cls=dataController,
164 inNamespace=True )
165 self.addLink( c, cs0 )
166 # Connect switch to root namespace so that data network
167 # switches will be able to talk to us
168 root = self.addHost( 'root', inNamespace=False )
169 self.addLink( root, cs0 )
170
171class ONOSCluster( Controller ):
172 # TODO
Brian O'Connor67eb3802014-10-22 19:55:38 -0700173 n = 3
Brian O'Connor195191b2014-10-22 01:09:36 -0700174
175 def start( self ):
176 ctopo = ControlNetwork( n=self.n, dataController=ONOS )
177 self.cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24', controller=None )
178 self.cnet.addController( 'cc0', controller=Controller )
179 self.cnet.start()
180
181 self.ctrls = []
182 for host in self.cnet.hosts:
183 if isinstance( host, Controller ):
184 self.ctrls.append( host )
185 host.start()
186
187 def stop( self ):
Brian O'Connor67eb3802014-10-22 19:55:38 -0700188 for host in self.cnet.hosts:
189 if isinstance( host, Controller ):
190 host.stop()
Brian O'Connor195191b2014-10-22 01:09:36 -0700191 self.cnet.stop()
192
193 def clist( self ):
194 "Return list of Controller proxies for this ONOS cluster"
195 print 'controllers:', self.ctrls
196 return self.ctrls
197
198class OVSSwitchONOS( OVSSwitch ):
199 "OVS switch which connects to multiple controllers"
200 def start( self, controllers ):
201 assert len( controllers ) == 1
202 c0 = controllers[ 0 ]
203 assert type( c0 ) == ONOSCluster
204 controllers = c0.clist()
205 OVSSwitch.start( self, controllers )
206
207controllers = { 'onos': ONOS }
208switches = { 'ovso': OVSSwitchONOS }
209
210if __name__ == '__main__':
211 # Simple test for ONOS() controller class
Brian O'Connor67eb3802014-10-22 19:55:38 -0700212 setLogLevel( 'info' ) #TODO info
Brian O'Connor195191b2014-10-22 01:09:36 -0700213 size = 2 if len( argv ) != 2 else int( argv[ 1 ] )
214 net = Mininet( topo=LinearTopo( size ),
Brian O'Connor67eb3802014-10-22 19:55:38 -0700215 #controller=ONOS,
216 controller=partial( ONOSCluster, n=3 ), #TODO
Brian O'Connor195191b2014-10-22 01:09:36 -0700217 switch=OVSSwitchONOS )
218 net.start()
219 #waitConnected( net.switches )
220 CLI( net )
221 net.stop()