blob: 7eebba48661181b11a2e76e8e1b09ae78cb779f3 [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')
Charles Chan45624b82015-08-24 00:29:20 +080034 #self.port = port (6653)
Brian O'Connor67eb3802014-10-22 19:55:38 -070035 #self.protocol = protocol ('tcp')
36 #self.checkListening()
37
38 self.onosDir = onosDir
Jian Li11599162016-01-15 15:46:16 -080039 self.karafDir = onosDir + 'apache-karaf-3.0.5/'
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'Connorb222e742016-04-20 22:39:44 -070065 instanceOpts = ( '-furl mvn:org.onosproject/onos-features/1.5.2-SNAPSHOT/xml/features '
Brian O'Connor67eb3802014-10-22 19:55:38 -070066 '-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
Jonathan Hart205f4b12015-01-19 14:18:10 -080090 self.userCmd( '%sbin/instance start -d %s' % ( self.karafDir, self.name ) )
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 ):
Jonathan Hart626ac962015-02-25 20:31:49 -0800100 hz = '192.168.123.*'
101 if self.ip is not None:
102 hz = '.'.join(self.ip.split('.')[:-1]) + '.*'
103
Brian O'Connor67eb3802014-10-22 19:55:38 -0700104 readfile = self.karafDir + 'etc/hazelcast.xml'
105 writefile = self.instanceDir + 'etc/hazelcast.xml'
106 with open( readfile, 'r' ) as r:
107 with open( writefile, 'w' ) as w:
108 for line in r.readlines():
109 if '<interface>' in line:
Jonathan Hart626ac962015-02-25 20:31:49 -0800110 line = '<interface>' + hz + '</interface>\n'
Brian O'Connor67eb3802014-10-22 19:55:38 -0700111 w.write( line )
112
113 def updateFeatures( self ):
114 filename = self.instanceDir + 'etc/org.apache.karaf.features.cfg'
Brian O'Connor195191b2014-10-22 01:09:36 -0700115 with open( filename, 'r+' ) as f:
116 lines = f.readlines()
117 f.seek(0)
118 f.truncate()
119 for line in lines:
120 #print '?', line,
121 if 'featuresBoot=' in line:
Brian O'Connor67eb3802014-10-22 19:55:38 -0700122 # parse the features from the line
123 features = line.rstrip().split('=')[1].split(',')
124 # add the features to our features set
125 self.features.update( features )
126 # generate the new features line
127 line = 'featuresBoot=' + ','.join( self.features ) + '\n'
Brian O'Connor195191b2014-10-22 01:09:36 -0700128 #print '!', line,
129 f.write( line )
130
Brian O'Connor67eb3802014-10-22 19:55:38 -0700131
Brian O'Connor195191b2014-10-22 01:09:36 -0700132 @classmethod
133 def isAvailable( self ):
Brian O'Connor67eb3802014-10-22 19:55:38 -0700134 return quietRun( 'ls %s' % self.onosDir )
135
136 def userCmd( self, cmd ):
137 # switch to the non-root user because karaf gets upset otherwise
138 # because the .m2repo is not stored with root
139 cmd = 'sudo -u %s %s' % ( self.findUser(), cmd )
140 return self.cmd( cmd )
Brian O'Connor195191b2014-10-22 01:09:36 -0700141
142 @staticmethod
143 def findUser():
144 "Try to return logged-in (usually non-root) user"
145 try:
146 # If we're running sudo
147 return os.environ[ 'SUDO_USER' ]
148 except:
149 try:
150 # Logged-in user (if we have a tty)
151 return quietRun( 'who am i' ).split()[ 0 ]
152 except:
153 # Give up and return effective user
154 return quietRun( 'whoami' )
155
156
157class ControlNetwork( Topo ):
158 "Control Network Topology"
159 def __init__( self, n, dataController=ONOS, **kwargs ):
160 """n: number of data network controller nodes
161 dataController: class for data network controllers"""
162 Topo.__init__( self, **kwargs )
163 # Connect everything to a single switch
164 cs0 = self.addSwitch( 'cs0' )
165 # Add hosts which will serve as data network controllers
Brian O'Connor67eb3802014-10-22 19:55:38 -0700166 for i in range( 1, n+1 ):
Brian O'Connor195191b2014-10-22 01:09:36 -0700167 c = self.addHost( 'c%s' % i, cls=dataController,
168 inNamespace=True )
169 self.addLink( c, cs0 )
170 # Connect switch to root namespace so that data network
171 # switches will be able to talk to us
172 root = self.addHost( 'root', inNamespace=False )
173 self.addLink( root, cs0 )
174
175class ONOSCluster( Controller ):
176 # TODO
Brian O'Connor67eb3802014-10-22 19:55:38 -0700177 n = 3
Brian O'Connor195191b2014-10-22 01:09:36 -0700178
179 def start( self ):
180 ctopo = ControlNetwork( n=self.n, dataController=ONOS )
181 self.cnet = Mininet( topo=ctopo, ipBase='192.168.123.0/24', controller=None )
182 self.cnet.addController( 'cc0', controller=Controller )
183 self.cnet.start()
184
185 self.ctrls = []
186 for host in self.cnet.hosts:
187 if isinstance( host, Controller ):
188 self.ctrls.append( host )
189 host.start()
190
191 def stop( self ):
Brian O'Connor67eb3802014-10-22 19:55:38 -0700192 for host in self.cnet.hosts:
193 if isinstance( host, Controller ):
194 host.stop()
Brian O'Connor195191b2014-10-22 01:09:36 -0700195 self.cnet.stop()
196
197 def clist( self ):
198 "Return list of Controller proxies for this ONOS cluster"
199 print 'controllers:', self.ctrls
200 return self.ctrls
201
202class OVSSwitchONOS( OVSSwitch ):
203 "OVS switch which connects to multiple controllers"
204 def start( self, controllers ):
205 assert len( controllers ) == 1
206 c0 = controllers[ 0 ]
207 assert type( c0 ) == ONOSCluster
208 controllers = c0.clist()
209 OVSSwitch.start( self, controllers )
210
211controllers = { 'onos': ONOS }
212switches = { 'ovso': OVSSwitchONOS }
213
214if __name__ == '__main__':
215 # Simple test for ONOS() controller class
Brian O'Connor67eb3802014-10-22 19:55:38 -0700216 setLogLevel( 'info' ) #TODO info
Brian O'Connor195191b2014-10-22 01:09:36 -0700217 size = 2 if len( argv ) != 2 else int( argv[ 1 ] )
218 net = Mininet( topo=LinearTopo( size ),
Brian O'Connor67eb3802014-10-22 19:55:38 -0700219 #controller=ONOS,
220 controller=partial( ONOSCluster, n=3 ), #TODO
Brian O'Connor195191b2014-10-22 01:09:36 -0700221 switch=OVSSwitchONOS )
222 net.start()
223 #waitConnected( net.switches )
224 CLI( net )
225 net.stop()