blob: 2efab0cd348ec14a514709f90d86ee8e8542f08e [file] [log] [blame]
Jon Hallca319892017-06-15 15:25:22 -07001"""
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07002Copyright 2017 Open Networking Foundation ( ONF )
Jon Hallca319892017-06-15 15:25:22 -07003
4Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
5the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
6or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
7
8 TestON is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070011 ( at your option ) any later version.
Jon Hallca319892017-06-15 15:25:22 -070012
13 TestON is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with TestON. If not, see <http://www.gnu.org/licenses/>.
20"""
Devin Lim3ebd5e72017-11-14 10:38:00 -080021import json
Jon Hallca319892017-06-15 15:25:22 -070022class Cluster():
23
24 def __str__( self ):
25 return self.name
Jon Hall4173b242017-09-12 17:04:38 -070026
Jon Hallca319892017-06-15 15:25:22 -070027 def __repr__( self ):
Jon Hallca319892017-06-15 15:25:22 -070028 controllers = []
Jon Hall3e6edb32018-08-21 16:20:30 -070029 runningNodes = self.getRunningNodes()
30 atomixNodes = []
Jon Hallca319892017-06-15 15:25:22 -070031 for ctrl in self.controllers:
Jon Hall3e6edb32018-08-21 16:20:30 -070032 controllers.append( "{%s:%s, %s - %s}" % ( ctrl.name,
33 ctrl.ipAddress,
34 "Configured" if ctrl in runningNodes else "Not Configured",
35 "Active" if ctrl.active else "Inactive" ) )
36 for node in self.atomixNodes:
37 atomixNodes.append( "{%s:%s}" % ( node.name, node.ipAddress ) )
38 return "%s[%s; Atomix Nodes:%s]" % ( self.name, ", ".join( controllers ), ", ".join( atomixNodes ) )
Jon Hallca319892017-06-15 15:25:22 -070039
Jon Hall3c0114c2020-08-11 15:07:42 -070040 def __init__( self, ctrlList=[], name="Cluster", useDocker=False ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070041 """
Devin Lim142b5342017-07-20 15:22:39 -070042 controllers : All the nodes
43 runningNodes : Node that are specifically running from the test.
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070044 ie ) When the test is testing different number of nodes on each
Devin Lim142b5342017-07-20 15:22:39 -070045 run.
46 numCtrls : number of runningNodes
47 maxCtrls : number of controllers
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070048 """
Jon Hallca319892017-06-15 15:25:22 -070049 self.controllers = ctrlList
Devin Lim142b5342017-07-20 15:22:39 -070050 self.runningNodes = ctrlList
51 self.numCtrls = len( self.runningNodes )
52 self.maxCtrls = len( self.controllers )
Jon Hallca319892017-06-15 15:25:22 -070053 self.name = str( name )
Jon Hall3e6edb32018-08-21 16:20:30 -070054 self.atomixNodes = ctrlList
Jon Hallca319892017-06-15 15:25:22 -070055 self.iterator = iter( self.active() )
Jon Hall3c0114c2020-08-11 15:07:42 -070056 self.useDocker = useDocker
57 clusterParams = main.params.get( "CLUSTER", {} )
58 self.dockerSkipBuild = clusterParams.get( "dockerSkipBuild", False )
59 self.dockerBuildCmd = clusterParams.get( "dockerBuildCmd", None )
60 self.dockerBuildTimeout = int( clusterParams.get( "dockerBuildTimeout", 600 ) )
61 self.dockerFilePath = clusterParams.get( "dockerFilePath", None )
62 self.dockerImageTag = clusterParams.get( "dockerImageTag", None )
63 self.dockerOptions = clusterParams.get( "dockerOptions", "" )
64 self.atomixImageTag = clusterParams.get( "atomixImageTag", None )
65 self.atomixOptions = clusterParams.get( "atomixOptions", "" )
Jon Hallca319892017-06-15 15:25:22 -070066
Jon Hall3e6edb32018-08-21 16:20:30 -070067 def fromNode( self, ctrlList ):
68 """
69 Helper function to get a specific list of controllers
70 Required Arguments:
71 * ctrlList - The list of controllers to return. This can be either an index or a keyword
72 Index | Keyword | List returned
73 0 | "all" | self.controllers
74 1 | "running" | self.runningNodes
75 2 | "active | self.active()
76
77 Throws a ValueError exception if ctrlList value is not recognized
78 """
79 # TODO: Add Atomix Nodes?
80 if isinstance( ctrlList, str ):
81 ctrlList = ctrlList.lower()
82 if ctrlList == 0 or ctrlList == "all":
83 return self.controllers
84 elif ctrlList == 1 or ctrlList == "running":
85 return self.runningNodes
86 elif ctrlList == 2 or ctrlList == "active":
87 return self.active()
88 else:
89 raise ValueError( "Unknown argument: {}".format( ctrlList ) )
90
Devin Lim142b5342017-07-20 15:22:39 -070091 def getIps( self, activeOnly=False, allNode=False ):
92 """
93 Description:
94 get the list of the ip. Default to get ips of running nodes.
95 Required:
96 * activeOnly - True for getting ips of active nodes
97 * allNode - True for getting ips of all nodes
98 Returns:
99 Retruns ips of active nodes if activeOnly is True.
100 Returns ips of all nodes if allNode is True.
101 """
Jon Hallca319892017-06-15 15:25:22 -0700102 ips = []
Devin Lim142b5342017-07-20 15:22:39 -0700103 if allNode:
Jon Hallca319892017-06-15 15:25:22 -0700104 nodeList = self.controllers
Devin Lim142b5342017-07-20 15:22:39 -0700105 else:
106 if activeOnly:
107 nodeList = self.active()
108 else:
109 nodeList = self.runningNodes
110
Jon Hallca319892017-06-15 15:25:22 -0700111 for ctrl in nodeList:
Jon Halld14a5002021-03-30 16:45:30 -0700112 ips.append( ctrl.ipAddress if 'localhost' not in ctrl.ipAddress else ctrl.address )
Devin Lim142b5342017-07-20 15:22:39 -0700113
Jon Hallca319892017-06-15 15:25:22 -0700114 return ips
115
Jon Halla1e8e512018-05-11 13:30:57 -0700116 def clearActive( self ):
Jon Hallca319892017-06-15 15:25:22 -0700117 """
Devin Lim142b5342017-07-20 15:22:39 -0700118 Description:
Jon Halla1e8e512018-05-11 13:30:57 -0700119 Sets the activeness of each cluster node to be False
Jon Hallca319892017-06-15 15:25:22 -0700120 """
Jon Hallab611372018-02-21 15:26:05 -0800121 for ctrl in self.controllers:
Devin Lim142b5342017-07-20 15:22:39 -0700122 ctrl.active = False
123
124 def getRunningPos( self ):
125 """
126 Description:
127 get the position of the active running nodes.
128 Required:
129 Returns:
130 Retruns the list of the position of the active
131 running nodes.
132 """
133 return [ ctrl.pos for ctrl in self.runningNodes
134 if ctrl.active ]
135
136 def setRunningNode( self, numCtrls ):
137 """
138 Description:
139 Set running nodes of n number of nodes.
140 It will create new list of runningNodes.
141 If numCtrls is a list, it will add the nodes of the
142 list.
143 Required:
144 * numCtrls - number of nodes to be set.
145 Returns:
146 """
147 self.runningNodes = []
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700148 for i in numCtrls if isinstance( numCtrls, list ) else range( numCtrls ):
Devin Lim142b5342017-07-20 15:22:39 -0700149 self.runningNodes.append( self.controllers[ i ] )
150 self.numCtrls = len( numCtrls ) if isinstance( numCtrls, list ) else numCtrls
151
Jon Hall3e6edb32018-08-21 16:20:30 -0700152 def setAtomixNodes( self, nodes ):
153 """
154 Description:
155 Sets the list of Atomix nodes for the cluster
156 If nodes is a list, it will add the nodes of the list.
157 If nodes is an int, the function will set the Atomix nodes
158 to be the first n in the list of controllers.
159 Required:
160 * nodes - number of nodes to be set, or a list of nodes to set
161 Returns:
162 """
163 self.atomixNodes = []
164 for i in nodes if isinstance( nodes, list ) else range( nodes ):
165 self.atomixNodes.append( self.controllers[ i ] )
166
167 def getControllers( self, node=None ):
168 """
169 Description:
170 Get the list of all controllers in a cluster or a controller at an index in the list
171 Optional Arguments:
172 * node - position of the node to get from the list of controllers.
173 Returns:
174 Return a list of controllers in the cluster if node is None
175 if not, it will return the controller at the given index.
176 """
177 result = self.controllers
178 return result if node is None else result[ node % len( result ) ]
179
180 def getRunningNodes( self, node=None ):
181 """
182 Description:
183 Get the list of all controllers in a cluster that should be running or
184 a controller at an index in the list
185 Optional Arguments:
186 * node - position of the node to get from the list of controllers.
187 Returns:
188 Return a list of controllers in the cluster if node is None
189 if not, it will return the controller at the given index.
190 """
191 result = self.runningNodes
192 return result if node is None else result[ node % len( result ) ]
193
Devin Lim142b5342017-07-20 15:22:39 -0700194 def active( self, node=None ):
195 """
196 Description:
Jon Hall3e6edb32018-08-21 16:20:30 -0700197 Get the list of all active controllers in a cluster or
198 a controller at an index in the list
199 Optional Arguments:
200 * node - position of the node to get from the list of controllers.
Devin Lim142b5342017-07-20 15:22:39 -0700201 Returns:
Jon Hall3e6edb32018-08-21 16:20:30 -0700202 Return a list of controllers in the cluster if node is None
203 if not, it will return the controller at the given index.
Devin Lim142b5342017-07-20 15:22:39 -0700204 """
205 result = [ ctrl for ctrl in self.runningNodes
Jon Hallca319892017-06-15 15:25:22 -0700206 if ctrl.active ]
Devin Lim142b5342017-07-20 15:22:39 -0700207 return result if node is None else result[ node % len( result ) ]
Jon Hallca319892017-06-15 15:25:22 -0700208
209 def next( self ):
210 """
Jon Halla1e8e512018-05-11 13:30:57 -0700211 An iterator for the cluster's active controllers that
Jon Hallca319892017-06-15 15:25:22 -0700212 resets when there are no more elements.
213
214 Returns the next controller in the cluster
215 """
216 try:
Jon Halla1e8e512018-05-11 13:30:57 -0700217 node = self.iterator.next()
218 assert node.active
219 return node
220 except ( StopIteration, AssertionError ):
Jon Hallca319892017-06-15 15:25:22 -0700221 self.reset()
222 try:
223 return self.iterator.next()
224 except StopIteration:
225 raise RuntimeError( "There are no active nodes in the cluster" )
226
227 def reset( self ):
228 """
229 Resets the cluster iterator.
230
231 This should be used whenever a node's active state is changed
232 and is also used internally when the iterator has been exhausted.
233 """
234 self.iterator = iter( self.active() )
235
Jon Hall3e6edb32018-08-21 16:20:30 -0700236 def createCell( self, cellName, cellApps, mininetIp, useSSH, onosIps,
237 atomixIps, installMax=False ):
Jon Hallca319892017-06-15 15:25:22 -0700238 """
Devin Lim142b5342017-07-20 15:22:39 -0700239 Description:
240 create a new cell
241 Required:
242 * cellName - The name of the cell.
You Wang09b596b2018-01-10 10:42:38 -0800243 * cellApps - The ONOS apps string.
You Wanga0f6ff62018-01-11 15:46:30 -0800244 * mininetIp - Mininet IP address.
Devin Lim142b5342017-07-20 15:22:39 -0700245 * useSSH - True for using ssh when creating a cell
Jon Hall3e6edb32018-08-21 16:20:30 -0700246 * onosIps - ip( s ) of the ONOS node( s ).
247 * atomixIps - ip( s ) of the Atomix node( s ).
248
Devin Lim142b5342017-07-20 15:22:39 -0700249 Returns:
250 """
Devin Lime9f0ccf2017-08-11 17:25:12 -0700251 self.command( "createCellFile",
252 args=[ main.ONOSbench.ip_address,
Devin Lim40a19092017-08-15 14:54:22 -0700253 cellName,
You Wanga0f6ff62018-01-11 15:46:30 -0800254 mininetIp,
You Wang0d9f2c02018-08-10 14:56:32 -0700255 cellApps,
Jon Hall3e6edb32018-08-21 16:20:30 -0700256 onosIps,
257 atomixIps,
Siddesh13492972021-03-12 21:09:32 +0000258 main.ONOScell.nodeUser,
Devin Lim40a19092017-08-15 14:54:22 -0700259 useSSH ],
Devin Lime9f0ccf2017-08-11 17:25:12 -0700260 specificDriver=1,
Jon Hall3e6edb32018-08-21 16:20:30 -0700261 getFrom="all" if installMax else "running" )
Devin Lim40a19092017-08-15 14:54:22 -0700262
You Wangf9d95be2018-08-01 14:35:37 -0700263 def uninstallAtomix( self, uninstallMax ):
264 """
265 Description:
266 uninstalling atomix
267 Required:
268 * uninstallMax - True for uninstalling max number of nodes
269 False for uninstalling the current running nodes.
270 Returns:
271 Returns main.TRUE if it successfully uninstalled.
272 """
273 result = main.TRUE
274 uninstallResult = self.command( "atomixUninstall",
275 kwargs={ "nodeIp": "ipAddress" },
276 specificDriver=1,
Jon Hall3e6edb32018-08-21 16:20:30 -0700277 getFrom="all" if uninstallMax else "running",
You Wangf9d95be2018-08-01 14:35:37 -0700278 funcFromCtrl=True )
279 for uninstallR in uninstallResult:
280 result = result and uninstallR
281 return result
282
283 def uninstallOnos( self, uninstallMax ):
Devin Lim142b5342017-07-20 15:22:39 -0700284 """
285 Description:
286 uninstalling onos
287 Required:
288 * uninstallMax - True for uninstalling max number of nodes
289 False for uninstalling the current running nodes.
290 Returns:
291 Returns main.TRUE if it successfully uninstalled.
292 """
Devin Lim40a19092017-08-15 14:54:22 -0700293 result = main.TRUE
294 uninstallResult = self.command( "onosUninstall",
Jon Hall4173b242017-09-12 17:04:38 -0700295 kwargs={ "nodeIp": "ipAddress" },
Devin Lim40a19092017-08-15 14:54:22 -0700296 specificDriver=1,
Jon Hall3e6edb32018-08-21 16:20:30 -0700297 getFrom="all" if uninstallMax else "running",
Devin Lim40a19092017-08-15 14:54:22 -0700298 funcFromCtrl=True )
299 for uninstallR in uninstallResult:
300 result = result and uninstallR
301 return result
Devin Lim142b5342017-07-20 15:22:39 -0700302
Devin Lime9f0ccf2017-08-11 17:25:12 -0700303 def applyCell( self, cellName, installMax=False ):
Devin Lim142b5342017-07-20 15:22:39 -0700304 """
305 Description:
306 apply the cell with cellName. It will also verify the
307 cell.
308 Required:
309 * cellName - The name of the cell.
310 Returns:
311 Returns main.TRUE if it successfully set and verify cell.
312 """
Jon Hall06fd0df2021-01-25 15:50:06 -0800313 result = main.TRUE
Devin Lime9f0ccf2017-08-11 17:25:12 -0700314 setCellResult = self.command( "setCell",
Jon Hall4173b242017-09-12 17:04:38 -0700315 args=[ cellName ],
316 specificDriver=1,
Jon Hall3e6edb32018-08-21 16:20:30 -0700317 getFrom="all" )
Devin Lime9f0ccf2017-08-11 17:25:12 -0700318 for i in range( len( setCellResult ) ):
Jon Hall06fd0df2021-01-25 15:50:06 -0800319 result = result and setCellResult[ i ]
320 benchCellResult = main.ONOSbench.setCell( cellName )
Jon Hall3e6edb32018-08-21 16:20:30 -0700321 result = result and benchCellResult
Jon Hall06fd0df2021-01-25 15:50:06 -0800322 if not self.useDocker:
323 verifyResult = self.command( "verifyCell",
324 specificDriver=1,
325 getFrom="all" )
326 for i in range( len( verifyResult ) ):
327 result = result and verifyResult[ i ]
Devin Lime9f0ccf2017-08-11 17:25:12 -0700328 return result
Devin Lim142b5342017-07-20 15:22:39 -0700329
330 def checkService( self ):
331 """
332 Description:
333 Checking if the onos service is up. If not, it will
334 start the onos service manually.
335 Required:
336 Returns:
337 Returns main.TRUE if it successfully checked
338 """
Jon Hall3e6edb32018-08-21 16:20:30 -0700339 getFrom = "running"
Devin Lim142b5342017-07-20 15:22:39 -0700340 onosIsUp = main.TRUE
Devin Lim40a19092017-08-15 14:54:22 -0700341 onosUp = self.command( "isup",
342 args=[ "ipAddress" ],
343 specificDriver=1,
Jon Hall3e6edb32018-08-21 16:20:30 -0700344 getFrom=getFrom,
Devin Lim40a19092017-08-15 14:54:22 -0700345 funcFromCtrl=True )
Jon Hall3e6edb32018-08-21 16:20:30 -0700346 ctrlList = self.fromNode( getFrom )
Devin Lim40a19092017-08-15 14:54:22 -0700347 for i in range( len( onosUp ) ):
Jon Hall3e6edb32018-08-21 16:20:30 -0700348 ctrl = ctrlList[ i ]
Devin Lim40a19092017-08-15 14:54:22 -0700349 onosIsUp = onosIsUp and onosUp[ i ]
350 if onosUp[ i ] == main.TRUE:
Jon Hall9655dcb2018-02-02 11:19:06 -0800351 main.log.info( ctrl.name + " is up and ready" )
Devin Lim142b5342017-07-20 15:22:39 -0700352 else:
Jon Hall9655dcb2018-02-02 11:19:06 -0800353 main.log.warn( ctrl.name + " may not be up." )
354 return onosIsUp
Devin Lim142b5342017-07-20 15:22:39 -0700355
You Wangf9d95be2018-08-01 14:35:37 -0700356 def killAtomix( self, killMax, stopAtomix ):
357 """
358 Description:
359 killing atomix. It will either kill the current runningnodes or
360 max number of the nodes.
361 Required:
362 * killRemoveMax - The boolean that will decide either to kill
363 only running nodes ( False ) or max number of nodes ( True ).
364 * stopAtomix - If wish to atomix onos before killing it. True for
365 enable stop, False for disable stop.
366 Returns:
367 Returns main.TRUE if successfully killing it.
368 """
369 result = main.TRUE
370 killResult = self.command( "atomixKill",
371 args=[ "ipAddress" ],
372 specificDriver=1,
Jon Hall3e6edb32018-08-21 16:20:30 -0700373 getFrom="all" if killMax else "running",
You Wangf9d95be2018-08-01 14:35:37 -0700374 funcFromCtrl=True )
375 for i in range( len( killResult ) ):
376 result = result and killResult[ i ]
You Wangf9d95be2018-08-01 14:35:37 -0700377 return result
378
379 def killOnos( self, killMax, stopOnos ):
Devin Lim142b5342017-07-20 15:22:39 -0700380 """
381 Description:
382 killing the onos. It will either kill the current runningnodes or
383 max number of the nodes.
384 Required:
385 * killRemoveMax - The boolean that will decide either to kill
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700386 only running nodes ( False ) or max number of nodes ( True ).
Devin Lim142b5342017-07-20 15:22:39 -0700387 * stopOnos - If wish to stop onos before killing it. True for
388 enable stop , False for disable stop.
389 Returns:
390 Returns main.TRUE if successfully killing it.
Jon Hallca319892017-06-15 15:25:22 -0700391 """
Jon Hall3e6edb32018-08-21 16:20:30 -0700392 getFrom = "all" if killMax else "running"
Jon Hallca319892017-06-15 15:25:22 -0700393 result = main.TRUE
Devin Lim40a19092017-08-15 14:54:22 -0700394 killResult = self.command( "onosKill",
395 args=[ "ipAddress" ],
396 specificDriver=1,
Jon Hall3e6edb32018-08-21 16:20:30 -0700397 getFrom=getFrom,
Devin Lim40a19092017-08-15 14:54:22 -0700398 funcFromCtrl=True )
Jon Hall3e6edb32018-08-21 16:20:30 -0700399 ctrlList = self.fromNode( getFrom )
Jon Hall4173b242017-09-12 17:04:38 -0700400 for i in range( len( killResult ) ):
Devin Lim40a19092017-08-15 14:54:22 -0700401 result = result and killResult[ i ]
Jon Hall3e6edb32018-08-21 16:20:30 -0700402 ctrlList[ i ].active = False
Devin Lim142b5342017-07-20 15:22:39 -0700403 return result
404
Jon Hall3c0114c2020-08-11 15:07:42 -0700405 def dockerStop( self, killMax, atomix=True ):
406 """
407 Description:
408 killing the onos docker containers. It will either kill the
409 current runningnodes or max number of the nodes.
410 Required:
411 * killRemoveMax - The boolean that will decide either to kill
412 only running nodes ( False ) or max number of nodes ( True ).
413 Returns:
414 Returns main.TRUE if successfully killing it.
415 """
416 getFrom = "all" if killMax else "running"
417 result = main.TRUE
418 stopResult = self.command( "dockerStop",
419 args=[ "name" ],
420 specificDriver=4,
421 getFrom=getFrom,
422 funcFromCtrl=True )
423 ctrlList = self.fromNode( getFrom )
424 for i in range( len( stopResult ) ):
425 result = result and stopResult[ i ]
426 ctrlList[ i ].active = False
427 atomixResult = main.TRUE
428 if atomix:
429 atomixResult = self.stopAtomixDocker( killMax )
430 return result and atomixResult
431
432 def dockerBuild( self, pull=True ):
433 """
434 Description:
435 Build ONOS docker image
436 Optional:
437 * pull - Try to pull latest image before building
438 Returns:
439 Returns main.TRUE if successfully killing it.
440 """
441 getFrom = "all"
442 result = main.TRUE
443 atomixResult = []
444 buildResult = []
445 if self.atomixImageTag:
446 atomixResult = self.command( "dockerPull",
447 args=[ self.atomixImageTag ],
448 specificDriver=4,
449 getFrom=getFrom,
450 funcFromCtrl=False )
451 if not self.dockerImageTag:
452 main.log.error( "No image given, exiting test" )
453 return main.FALSE
454 if pull and self.dockerImageTag:
455 buildResult = self.command( "dockerPull",
456 args=[ self.dockerImageTag ],
457 specificDriver=4,
458 getFrom=getFrom,
459 funcFromCtrl=False )
460 for i in range( len( buildResult ) ):
461 result = result and buildResult[ i ]
462 if self.dockerSkipBuild:
463 return main.TRUE
464 if not result and self.dockerBuildCmd:
465 buildResult = self.command( "makeDocker",
466 args=[ self.dockerFilePath, self.dockerBuildCmd ],
467 kwargs={ "timeout": self.dockerBuildTimeout,
468 "prompt": "Successfully tagged %s" % self.dockerImageTag },
469 specificDriver=4,
470 getFrom=getFrom,
471 funcFromCtrl=False )
472
473 elif not result:
474 buildResult = self.command( "dockerBuild",
475 args=[ self.dockerFilePath, self.dockerImageTag ],
476 kwargs={ "timeout": self.dockerBuildTimeout,
477 "pull": pull },
478 specificDriver=4,
479 getFrom=getFrom,
480 funcFromCtrl=False )
481 for i in range( len( atomixResult ) ):
482 result = result and atomixResult[ i ]
483 for i in range( len( buildResult ) ):
484 result = result and buildResult[ i ]
485 return result
486
Devin Lim142b5342017-07-20 15:22:39 -0700487 def ssh( self ):
488 """
489 Description:
490 set up ssh to the onos
491 Required:
492 Returns:
493 Returns main.TRUE if it successfully setup the ssh to
494 the onos.
495 """
Devin Lim40a19092017-08-15 14:54:22 -0700496 result = main.TRUE
Jon Hall3c0114c2020-08-11 15:07:42 -0700497 if self.useDocker:
498 driver = 2
499 kwargs = { "userName": "karafUser",
500 "userPWD": "karafPass" }
501 else:
502 driver = 1
503 kwargs = { "node": "ipAddress" }
Devin Lim40a19092017-08-15 14:54:22 -0700504 sshResult = self.command( "onosSecureSSH",
Jon Hall3c0114c2020-08-11 15:07:42 -0700505 kwargs=kwargs,
506 specificDriver=driver,
Jon Hall3e6edb32018-08-21 16:20:30 -0700507 getFrom="running",
Devin Lim40a19092017-08-15 14:54:22 -0700508 funcFromCtrl=True )
509 for sshR in sshResult:
510 result = result and sshR
511 return result
Devin Lim142b5342017-07-20 15:22:39 -0700512
Jon Hall3e6edb32018-08-21 16:20:30 -0700513 def installAtomix( self, installParallel=True ):
You Wangf9d95be2018-08-01 14:35:37 -0700514 """
515 Description:
Jon Hall3e6edb32018-08-21 16:20:30 -0700516 Installing Atomix.
You Wangf9d95be2018-08-01 14:35:37 -0700517 Required:
You Wangf9d95be2018-08-01 14:35:37 -0700518 Returns:
519 Returns main.TRUE if it successfully installed
520 """
521 result = main.TRUE
Jon Hall3c0114c2020-08-11 15:07:42 -0700522 if self.useDocker:
523 # We will do this as part of startDocker
524 return result
You Wangf9d95be2018-08-01 14:35:37 -0700525 threads = []
526 i = 0
Jon Hall3e6edb32018-08-21 16:20:30 -0700527 for ctrl in self.atomixNodes:
528 options = "-f"
You Wangf9d95be2018-08-01 14:35:37 -0700529 if installParallel:
530 t = main.Thread( target=ctrl.Bench.atomixInstall,
531 name="atomix-install-" + ctrl.name,
532 kwargs={ "node" : ctrl.ipAddress,
533 "options" : options } )
534 threads.append( t )
535 t.start()
536 else:
537 result = result and \
538 main.ONOSbench.atomixInstall( node=ctrl.ipAddress, options=options )
539 i += 1
540 if installParallel:
541 for t in threads:
542 t.join()
543 result = result and t.result
544 return result
545
Jon Hall23bbe852021-03-26 10:47:23 -0700546 def installOnos( self, installMax=True, installParallel=True, retries=5 ):
Devin Lim142b5342017-07-20 15:22:39 -0700547 """
548 Description:
549 Installing onos.
550 Required:
551 * installMax - True for installing max number of nodes
552 False for installing current running nodes only.
553 Returns:
554 Returns main.TRUE if it successfully installed
555 """
556 result = main.TRUE
557 threads = []
558 i = 0
559 for ctrl in self.controllers if installMax else self.runningNodes:
560 options = "-f"
561 if installMax and i >= self.numCtrls:
562 options = "-nf"
Jon Hall23bbe852021-03-26 10:47:23 -0700563 args = [ ctrl.Bench.onosInstall, main.FALSE ]
564 kwargs={ "node" : ctrl.ipAddress,
565 "options" : options }
Devin Lime9f0ccf2017-08-11 17:25:12 -0700566 if installParallel:
Jon Hall23bbe852021-03-26 10:47:23 -0700567 t = main.Thread( target=utilities.retry,
You Wangf9d95be2018-08-01 14:35:37 -0700568 name="onos-install-" + ctrl.name,
Jon Hall23bbe852021-03-26 10:47:23 -0700569 args=args,
570 kwargs={ 'kwargs': kwargs,
571 'attempts': retries } )
572
Devin Lime9f0ccf2017-08-11 17:25:12 -0700573 threads.append( t )
574 t.start()
575 else:
576 result = result and \
Jon Hall23bbe852021-03-26 10:47:23 -0700577 utilities.retry( args=args, kwargs=kwargs, attempts=retries )
Jon Halla6771b32018-01-26 16:07:28 -0800578 i += 1
Devin Lime9f0ccf2017-08-11 17:25:12 -0700579 if installParallel:
580 for t in threads:
581 t.join()
582 result = result and t.result
Jon Hallca319892017-06-15 15:25:22 -0700583 return result
584
Jon Hall3c0114c2020-08-11 15:07:42 -0700585 def startONOSDocker( self, installMax=True, installParallel=True ):
586 """
587 Description:
588 Installing onos via docker containers.
589 Required:
590 * installMax - True for installing max number of nodes
591 False for installing current running nodes only.
592 Returns:
593 Returns main.TRUE if it successfully installed
594 """
595 result = main.TRUE
596 threads = []
597 for ctrl in self.controllers if installMax else self.runningNodes:
598 if installParallel:
599 t = main.Thread( target=ctrl.server.dockerRun,
600 name="onos-run-docker-" + ctrl.name,
601 args=[ self.dockerImageTag, ctrl.name ],
602 kwargs={ "options" : self.dockerOptions } )
603 threads.append( t )
604 t.start()
605 else:
606 result = result and \
607 ctrl.server.dockerRun( self.dockerImageTag,
608 ctrl.name,
609 options=self.dockerOptions )
610 if installParallel:
611 for t in threads:
612 t.join()
613 result = result and t.result
614 return result
615
Jon Hall214f88b2020-09-21 10:21:42 -0700616 def startONOSDockerNode( self, node ):
617 """
618 Description:
619 Installing onos via docker container on a specific node.
620 Required:
621 * node - the node to install ONOS on, given in index of runningNodes
622 Returns:
623 Returns main.TRUE if it successfully installed
624 """
625 ctrl = self.runningNodes[ node ]
626 result = ctrl.server.dockerRun( self.dockerImageTag,
627 ctrl.name,
628 options=self.dockerOptions )
629 return result
630
Jon Hall3c0114c2020-08-11 15:07:42 -0700631 def startAtomixDocker( self, installParallel=True ):
632 """
633 Description:
634 Installing atomix via docker containers.
635 Required:
636 * installParallel - True for installing atomix in parallel.
637 Returns:
638 Returns main.TRUE if it successfully installed
639 """
640 result = main.TRUE
641 threads = []
642 for ctrl in self.atomixNodes:
643 if installParallel:
644 t = main.Thread( target=ctrl.server.dockerRun,
645 name="atomix-run-docker-" + ctrl.name,
646 args=[ self.atomixImageTag, "atomix-" + ctrl.name ],
647 kwargs={ "options" : main.params['CLUSTER']['atomixOptions'],
648 "imageArgs": " --config /opt/atomix/conf/atomix.json --ignore-resources"} )
649 threads.append( t )
650 t.start()
651 else:
652 result = result and \
653 ctrl.server.dockerRun( self.atomixImageTag,
654 "atomix-" + ctrl.name,
655 options=main.params['CLUSTER']['atomixOptions'] )
656 if installParallel:
657 for t in threads:
658 t.join()
659 result = result and t.result
660 return result
661
662 def stopAtomixDocker( self, killMax=True, installParallel=True ):
663 """
664 Description:
665 Stoping all atomix containers
666 Required:
667 * killMax - True for stoping max number of nodes
668 False for stoping current running nodes only.
669 Returns:
670 Returns main.TRUE if it successfully stoped
671 """
672 result = main.TRUE
673 threads = []
674 for ctrl in self.controllers if killMax else self.atomixNodes:
675 if installParallel:
676 t = main.Thread( target=ctrl.server.dockerStop,
677 name="atomix-stop-docker-" + ctrl.name,
678 args=[ "atomix-" + ctrl.name ] )
679 threads.append( t )
680 t.start()
681 else:
682 result = result and \
683 ctrl.server.dockerStop( "atomix-" + ctrl.name )
684 if installParallel:
685 for t in threads:
686 t.join()
687 result = result and t.result
688 return result
689
690 def genPartitions( self, path="/tmp/cluster.json" ):
691 """
692 Description:
693 Create cluster config and move to each onos server
694 Required:
695 * installMax - True for installing max number of nodes
696 False for installing current running nodes only.
697 Returns:
698 Returns main.TRUE if it successfully installed
699 """
700 result = main.TRUE
701 # move files to onos servers
702 for ctrl in self.atomixNodes:
703 localAtomixFile = ctrl.ip_address + "-atomix.json"
704 result = result and main.ONOSbench.generateAtomixConfig( ctrl.server.ip_address, path=localAtomixFile )
705 result = result and main.ONOSbench.scp( ctrl.server,
706 localAtomixFile,
707 "/tmp/atomix.json",
708 direction="to" )
709 for ctrl in self.controllers:
710 localOnosFile = ctrl.ip_address + "-cluster.json"
711 result = result and main.ONOSbench.generateOnosConfig( ctrl.server.ip_address, path=localOnosFile )
712 result = result and main.ONOSbench.scp( ctrl.server,
713 localOnosFile,
714 path,
715 direction="to" )
716 return result
717
Jon Hallca319892017-06-15 15:25:22 -0700718 def startCLIs( self ):
719 """
Devin Lim142b5342017-07-20 15:22:39 -0700720 Description:
721 starting Onos using onosCli driver
722 Required:
723 Returns:
724 Returns main.TRUE if it successfully started.
Jon Hallca319892017-06-15 15:25:22 -0700725 """
Jon Hall3e6edb32018-08-21 16:20:30 -0700726 getFrom = "running"
Devin Lim40a19092017-08-15 14:54:22 -0700727 result = main.TRUE
728 cliResults = self.command( "startOnosCli",
729 args=[ "ipAddress" ],
Jon Hall9b0de1f2020-08-24 15:38:04 -0700730 kwargs= { "karafTimeout": "karafTimeout" },
Devin Lim40a19092017-08-15 14:54:22 -0700731 specificDriver=2,
Jon Hall3e6edb32018-08-21 16:20:30 -0700732 getFrom=getFrom,
Devin Lim40a19092017-08-15 14:54:22 -0700733 funcFromCtrl=True )
Jon Hall3e6edb32018-08-21 16:20:30 -0700734 ctrlList = self.fromNode( getFrom )
Jon Hall4173b242017-09-12 17:04:38 -0700735 for i in range( len( cliResults ) ):
Devin Lim40a19092017-08-15 14:54:22 -0700736 result = result and cliResults[ i ]
Jon Hall3e6edb32018-08-21 16:20:30 -0700737 ctrlList[ i ].active = True
Devin Lim40a19092017-08-15 14:54:22 -0700738 return result
Jon Hallca319892017-06-15 15:25:22 -0700739
Devin Lim3ebd5e72017-11-14 10:38:00 -0800740 def nodesCheck( self ):
You Wang0d9f2c02018-08-10 14:56:32 -0700741 """
742 Description:
743 Checking if all the onos nodes are in READY state
744 Required:
745 Returns:
746 Returns True if it successfully checked
747 """
Siddesh13492972021-03-12 21:09:32 +0000748 import time
Devin Lim3ebd5e72017-11-14 10:38:00 -0800749 results = True
750 nodesOutput = self.command( "nodes", specificDriver=2 )
Siddesh52750622021-03-05 19:52:03 +0000751 if None in nodesOutput:
Siddesh13492972021-03-12 21:09:32 +0000752 time.sleep(60)
753 nodesOutput = self.command( "nodes", specificDriver=2 )
754 main.log.debug(nodesOutput)
755 if None in nodesOutput:
756 return False
Siddesh52750622021-03-05 19:52:03 +0000757 try:
758 self.command( "getAddress", specificDriver=2 )
759 except ( ValueError, TypeError ):
760 main.log.error( "Error parsing summary output" )
761 currentResult = False
762 return currentResult
Devin Lim3ebd5e72017-11-14 10:38:00 -0800763 ips = sorted( self.getIps( activeOnly=True ) )
764 for i in nodesOutput:
765 try:
766 current = json.loads( i )
767 activeIps = []
768 currentResult = False
769 for node in current:
770 if node[ 'state' ] == 'READY':
771 activeIps.append( node[ 'ip' ] )
772 activeIps.sort()
773 if ips == activeIps:
774 currentResult = True
Jon Hall3e6edb32018-08-21 16:20:30 -0700775 else:
Jon Hall627b1572020-12-01 12:01:15 -0800776 main.log.warn( "Expected {} != found {}".format( ips, activeIps ) )
Devin Lim3ebd5e72017-11-14 10:38:00 -0800777 except ( ValueError, TypeError ):
778 main.log.error( "Error parsing nodes output" )
779 main.log.warn( repr( i ) )
780 currentResult = False
781 results = results and currentResult
Jon Hall3c0114c2020-08-11 15:07:42 -0700782 # Check to make sure all bundles are started
783 bundleOutput = self.command( "sendline", args=[ "bundle:list" ] )
784 for i in bundleOutput:
785 if "START LEVEL 100" in i:
786 currentResult = True
787 else:
788 currentResult = False
789 main.log.warn( "Node's bundles not fully started" )
790 main.log.debug( i )
791 results = results and currentResult
Devin Lim3ebd5e72017-11-14 10:38:00 -0800792 return results
793
You Wang0d9f2c02018-08-10 14:56:32 -0700794 def appsCheck( self, apps ):
795 """
796 Description:
797 Checking if all the applications are activated
798 Required:
799 apps: list of applications that are expected to be activated
800 Returns:
801 Returns True if it successfully checked
802 """
803 results = True
804 for app in apps:
805 states = self.command( "appStatus",
806 args=[ app ],
807 specificDriver=2 )
808 for i in range( len( states ) ):
809 ctrl = self.controllers[ i ]
810 if states[ i ] == "ACTIVE":
811 results = results and True
812 main.log.info( "{}: {} is activated".format( ctrl.name, app ) )
813 else:
814 results = False
815 main.log.warn( "{}: {} is in {} state".format( ctrl.name, app, states[ i ] ) )
816 return results
817
Jon Hall3c0114c2020-08-11 15:07:42 -0700818 def attachToONOSDocker( self ):
819 """
820 Description:
821 connect to onos docker using onosCli driver
822 Required:
823 Returns:
824 Returns main.TRUE if it successfully started.
825 """
826 getFrom = "running"
827 result = main.TRUE
828 execResults = self.command( "dockerExec",
829 args=[ "name" ],
830 kwargs={ "dockerPrompt": "dockerPrompt" },
831 specificDriver=2,
832 getFrom=getFrom,
833 funcFromCtrl=True )
834 ctrlList = self.fromNode( getFrom )
835 for i in range( len( execResults ) ):
836 result = result and execResults[ i ]
837 ctrlList[ i ].active = True
838 return result
839
840 def prepareForCLI( self ):
841 """
842 Description:
843 prepare docker to connect to the onos cli
844 Required:
845 Returns:
846 Returns main.TRUE if it successfully started.
847 """
848 getFrom = "running"
849 for ctrl in self.getRunningNodes():
850 ctrl.CLI.inDocker = True
851 result = main.TRUE
852 execResults = self.command( "prepareForCLI",
853 specificDriver=2,
854 getFrom=getFrom,
855 funcFromCtrl=True )
856 ctrlList = self.fromNode( getFrom )
857 for i in range( len( execResults ) ):
858 result = result and execResults[ i ]
859 ctrlList[ i ].active = True
860 return result
861
Devin Lim142b5342017-07-20 15:22:39 -0700862 def printResult( self, results, activeList, logLevel="debug" ):
863 """
864 Description:
865 Print the value of the list.
866 Required:
867 * results - list of the result
868 * activeList - list of the acitve nodes.
869 * logLevel - Type of log level you want it to be printed.
870 Returns:
871 """
872 f = getattr( main.log, logLevel )
Jon Hall4173b242017-09-12 17:04:38 -0700873 for i in range( len( results ) ):
874 f( activeList[ i ].name + "'s result : " + str( results[ i ] ) )
Devin Lim142b5342017-07-20 15:22:39 -0700875
876 def allTrueResultCheck( self, results, activeList ):
877 """
878 Description:
879 check if all the result has main.TRUE.
880 Required:
881 * results - list of the result
882 * activeList - list of the acitve nodes.
883 Returns:
884 Returns True if all == main.TRUE else
885 returns False
886 """
887 self.printResult( results, activeList )
888 return all( result == main.TRUE for result in results )
889
890 def notEmptyResultCheck( self, results, activeList ):
891 """
892 Description:
893 check if all the result has any contents
894 Required:
895 * results - list of the result
896 * activeList - list of the acitve nodes.
897 Returns:
898 Returns True if all the results has
899 something else returns False
900 """
901 self.printResult( results, activeList )
902 return all( result for result in results )
903
904 def identicalResultsCheck( self, results, activeList ):
905 """
906 Description:
907 check if all the results has same output.
908 Required:
909 * results - list of the result
910 * activeList - list of the acitve nodes.
911 Returns:
912 Returns True if all the results has
913 same result else returns False
914 """
915 self.printResult( results, activeList )
916 resultOne = results[ 0 ]
917 return all( resultOne == result for result in results )
918
Devin Lime9f0ccf2017-08-11 17:25:12 -0700919 def command( self, function, args=(), kwargs={}, returnBool=False,
Jon Hall3e6edb32018-08-21 16:20:30 -0700920 specificDriver=0, contentCheck=False, getFrom="active",
Devin Lim40a19092017-08-15 14:54:22 -0700921 funcFromCtrl=False ):
Devin Lim142b5342017-07-20 15:22:39 -0700922 """
923 Description:
924 execute some function of the active nodes.
925 Required:
926 * function - name of the function
927 * args - argument of the function
928 * kwargs - kwargs of the funciton
929 * returnBool - True if wish to check all the result has main.TRUE
930 * specificDriver - specific driver to execute the function. Since
931 some of the function can be used in different drivers, it is important
932 to specify which driver it will be executed from.
933 0 - any type of driver
934 1 - from bench
935 2 - from cli
936 3 - from rest
Jon Hall3c0114c2020-08-11 15:07:42 -0700937 4 - from server
Devin Lim142b5342017-07-20 15:22:39 -0700938 * contentCheck - If this is True, it will check if the result has some
939 contents.
Devin Lime9f0ccf2017-08-11 17:25:12 -0700940 * getFrom - from which nodes
Jon Hall3e6edb32018-08-21 16:20:30 -0700941 2 or "active" - active nodes
942 1 or "running" - current running nodes
943 0 or "all" - all nodes
Devin Lim40a19092017-08-15 14:54:22 -0700944 * funcFromCtrl - specific function of the args/kwargs
945 from each controller from the list of the controllers
Devin Lim142b5342017-07-20 15:22:39 -0700946 Returns:
947 Returns results if not returnBool and not contentCheck
948 Returns checkTruthValue of the result if returnBool
949 Returns resultContent of the result if contentCheck
950 """
Jon Hallca319892017-06-15 15:25:22 -0700951 threads = []
Jon Hall3c0114c2020-08-11 15:07:42 -0700952 drivers = [ None, "Bench", "CLI", "REST", "server" ]
Jon Hallca319892017-06-15 15:25:22 -0700953 results = []
Jon Hall3e6edb32018-08-21 16:20:30 -0700954 for ctrl in self.fromNode( getFrom ):
Jon Hall3c0114c2020-08-11 15:07:42 -0700955 funcArgs = []
956 funcKwargs = {}
Devin Lim142b5342017-07-20 15:22:39 -0700957 try:
958 f = getattr( ( ctrl if not specificDriver else
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700959 getattr( ctrl, drivers[ specificDriver ] ) ), function )
Devin Lim142b5342017-07-20 15:22:39 -0700960 except AttributeError:
961 main.log.error( "Function " + function + " not found. Exiting the Test." )
Devin Lim44075962017-08-11 10:56:37 -0700962 main.cleanAndExit()
Jon Hall3c0114c2020-08-11 15:07:42 -0700963 if funcFromCtrl:
964 if args:
965 try:
966 for i in range( len( args ) ):
967 funcArgs.append( getattr( ctrl, args[ i ] ) )
968 except AttributeError:
969 main.log.error( "Argument " + str( args[ i ] ) + " for " + str( f ) + " not found. Exiting the Test." )
970 main.cleanAndExit()
971 if kwargs:
972 try:
973 for k in kwargs:
974 funcKwargs.update( { k: getattr( ctrl, kwargs[ k ] ) } )
975 except AttributeError as e:
976 main.log.exception("")
977 main.log.error( "Keyword Argument " + str( k ) + " for " + str( f ) + " not found. Exiting the Test." )
978 main.log.debug( "Passed kwargs: %s; dir(ctrl): %s" % ( repr( kwargs ), dir( ctrl ) ) )
979 main.cleanAndExit()
Jon Hallca319892017-06-15 15:25:22 -0700980 t = main.Thread( target=f,
981 name=function + "-" + ctrl.name,
Devin Lim40a19092017-08-15 14:54:22 -0700982 args=funcArgs if funcFromCtrl else args,
983 kwargs=funcKwargs if funcFromCtrl else kwargs )
Jon Hallca319892017-06-15 15:25:22 -0700984 threads.append( t )
985 t.start()
986
987 for t in threads:
988 t.join()
989 results.append( t.result )
Devin Lim142b5342017-07-20 15:22:39 -0700990 if returnBool:
Jon Hall3e6edb32018-08-21 16:20:30 -0700991 return self.allTrueResultCheck( results, self.fromNode( getFrom ) )
Devin Lim142b5342017-07-20 15:22:39 -0700992 elif contentCheck:
Jon Hall3e6edb32018-08-21 16:20:30 -0700993 return self.notEmptyResultCheck( results, self.fromNode( getFrom ) )
Jon Hall4173b242017-09-12 17:04:38 -0700994 return results
995
996 def checkPartitionSize( self, segmentSize='64', units='M', multiplier='3' ):
997 # max segment size in bytes: 1024 * 1024 * 64
998 # multiplier is somewhat arbitrary, but the idea is the logs would have
999 # been compacted before this many segments are written
1000
Jon Hall43060f62020-06-23 13:13:33 -07001001 try:
1002 maxSize = float( segmentSize ) * float( multiplier )
1003 ret = True
1004 for n in self.runningNodes:
1005 # Partition logs
1006 ret = ret and n.server.folderSize( "/opt/atomix/data/raft/partitions/*/*.log",
1007 size=maxSize, unit=units, ignoreRoot=False )
1008 return ret
1009 except Exception as e:
1010 main.log.error( e )
1011 main.log.error( repr( e ) )