blob: b905381cf07cd35ef651e1d0326d5c6b556bd682 [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 Hall06fd0df2021-01-25 15:50:06 -0800112 ips.append( ctrl.ipAddress if ctrl.ipAddress is not 'localhost' 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,
Devin Lim40a19092017-08-15 14:54:22 -0700258 main.ONOScell.karafUser,
259 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 """
Devin Lim3ebd5e72017-11-14 10:38:00 -0800748 results = True
749 nodesOutput = self.command( "nodes", specificDriver=2 )
Siddesh52750622021-03-05 19:52:03 +0000750 if None in nodesOutput:
751 return False
752 try:
753 self.command( "getAddress", specificDriver=2 )
754 except ( ValueError, TypeError ):
755 main.log.error( "Error parsing summary output" )
756 currentResult = False
757 return currentResult
Devin Lim3ebd5e72017-11-14 10:38:00 -0800758 ips = sorted( self.getIps( activeOnly=True ) )
759 for i in nodesOutput:
760 try:
761 current = json.loads( i )
762 activeIps = []
763 currentResult = False
764 for node in current:
765 if node[ 'state' ] == 'READY':
766 activeIps.append( node[ 'ip' ] )
767 activeIps.sort()
768 if ips == activeIps:
769 currentResult = True
Jon Hall3e6edb32018-08-21 16:20:30 -0700770 else:
Jon Hall627b1572020-12-01 12:01:15 -0800771 main.log.warn( "Expected {} != found {}".format( ips, activeIps ) )
Devin Lim3ebd5e72017-11-14 10:38:00 -0800772 except ( ValueError, TypeError ):
773 main.log.error( "Error parsing nodes output" )
774 main.log.warn( repr( i ) )
775 currentResult = False
776 results = results and currentResult
Jon Hall3c0114c2020-08-11 15:07:42 -0700777 # Check to make sure all bundles are started
778 bundleOutput = self.command( "sendline", args=[ "bundle:list" ] )
779 for i in bundleOutput:
780 if "START LEVEL 100" in i:
781 currentResult = True
782 else:
783 currentResult = False
784 main.log.warn( "Node's bundles not fully started" )
785 main.log.debug( i )
786 results = results and currentResult
Devin Lim3ebd5e72017-11-14 10:38:00 -0800787 return results
788
You Wang0d9f2c02018-08-10 14:56:32 -0700789 def appsCheck( self, apps ):
790 """
791 Description:
792 Checking if all the applications are activated
793 Required:
794 apps: list of applications that are expected to be activated
795 Returns:
796 Returns True if it successfully checked
797 """
798 results = True
799 for app in apps:
800 states = self.command( "appStatus",
801 args=[ app ],
802 specificDriver=2 )
803 for i in range( len( states ) ):
804 ctrl = self.controllers[ i ]
805 if states[ i ] == "ACTIVE":
806 results = results and True
807 main.log.info( "{}: {} is activated".format( ctrl.name, app ) )
808 else:
809 results = False
810 main.log.warn( "{}: {} is in {} state".format( ctrl.name, app, states[ i ] ) )
811 return results
812
Jon Hall3c0114c2020-08-11 15:07:42 -0700813 def attachToONOSDocker( self ):
814 """
815 Description:
816 connect to onos docker using onosCli driver
817 Required:
818 Returns:
819 Returns main.TRUE if it successfully started.
820 """
821 getFrom = "running"
822 result = main.TRUE
823 execResults = self.command( "dockerExec",
824 args=[ "name" ],
825 kwargs={ "dockerPrompt": "dockerPrompt" },
826 specificDriver=2,
827 getFrom=getFrom,
828 funcFromCtrl=True )
829 ctrlList = self.fromNode( getFrom )
830 for i in range( len( execResults ) ):
831 result = result and execResults[ i ]
832 ctrlList[ i ].active = True
833 return result
834
835 def prepareForCLI( self ):
836 """
837 Description:
838 prepare docker to connect to the onos cli
839 Required:
840 Returns:
841 Returns main.TRUE if it successfully started.
842 """
843 getFrom = "running"
844 for ctrl in self.getRunningNodes():
845 ctrl.CLI.inDocker = True
846 result = main.TRUE
847 execResults = self.command( "prepareForCLI",
848 specificDriver=2,
849 getFrom=getFrom,
850 funcFromCtrl=True )
851 ctrlList = self.fromNode( getFrom )
852 for i in range( len( execResults ) ):
853 result = result and execResults[ i ]
854 ctrlList[ i ].active = True
855 return result
856
Devin Lim142b5342017-07-20 15:22:39 -0700857 def printResult( self, results, activeList, logLevel="debug" ):
858 """
859 Description:
860 Print the value of the list.
861 Required:
862 * results - list of the result
863 * activeList - list of the acitve nodes.
864 * logLevel - Type of log level you want it to be printed.
865 Returns:
866 """
867 f = getattr( main.log, logLevel )
Jon Hall4173b242017-09-12 17:04:38 -0700868 for i in range( len( results ) ):
869 f( activeList[ i ].name + "'s result : " + str( results[ i ] ) )
Devin Lim142b5342017-07-20 15:22:39 -0700870
871 def allTrueResultCheck( self, results, activeList ):
872 """
873 Description:
874 check if all the result has main.TRUE.
875 Required:
876 * results - list of the result
877 * activeList - list of the acitve nodes.
878 Returns:
879 Returns True if all == main.TRUE else
880 returns False
881 """
882 self.printResult( results, activeList )
883 return all( result == main.TRUE for result in results )
884
885 def notEmptyResultCheck( self, results, activeList ):
886 """
887 Description:
888 check if all the result has any contents
889 Required:
890 * results - list of the result
891 * activeList - list of the acitve nodes.
892 Returns:
893 Returns True if all the results has
894 something else returns False
895 """
896 self.printResult( results, activeList )
897 return all( result for result in results )
898
899 def identicalResultsCheck( self, results, activeList ):
900 """
901 Description:
902 check if all the results has same output.
903 Required:
904 * results - list of the result
905 * activeList - list of the acitve nodes.
906 Returns:
907 Returns True if all the results has
908 same result else returns False
909 """
910 self.printResult( results, activeList )
911 resultOne = results[ 0 ]
912 return all( resultOne == result for result in results )
913
Devin Lime9f0ccf2017-08-11 17:25:12 -0700914 def command( self, function, args=(), kwargs={}, returnBool=False,
Jon Hall3e6edb32018-08-21 16:20:30 -0700915 specificDriver=0, contentCheck=False, getFrom="active",
Devin Lim40a19092017-08-15 14:54:22 -0700916 funcFromCtrl=False ):
Devin Lim142b5342017-07-20 15:22:39 -0700917 """
918 Description:
919 execute some function of the active nodes.
920 Required:
921 * function - name of the function
922 * args - argument of the function
923 * kwargs - kwargs of the funciton
924 * returnBool - True if wish to check all the result has main.TRUE
925 * specificDriver - specific driver to execute the function. Since
926 some of the function can be used in different drivers, it is important
927 to specify which driver it will be executed from.
928 0 - any type of driver
929 1 - from bench
930 2 - from cli
931 3 - from rest
Jon Hall3c0114c2020-08-11 15:07:42 -0700932 4 - from server
Devin Lim142b5342017-07-20 15:22:39 -0700933 * contentCheck - If this is True, it will check if the result has some
934 contents.
Devin Lime9f0ccf2017-08-11 17:25:12 -0700935 * getFrom - from which nodes
Jon Hall3e6edb32018-08-21 16:20:30 -0700936 2 or "active" - active nodes
937 1 or "running" - current running nodes
938 0 or "all" - all nodes
Devin Lim40a19092017-08-15 14:54:22 -0700939 * funcFromCtrl - specific function of the args/kwargs
940 from each controller from the list of the controllers
Devin Lim142b5342017-07-20 15:22:39 -0700941 Returns:
942 Returns results if not returnBool and not contentCheck
943 Returns checkTruthValue of the result if returnBool
944 Returns resultContent of the result if contentCheck
945 """
Jon Hallca319892017-06-15 15:25:22 -0700946 threads = []
Jon Hall3c0114c2020-08-11 15:07:42 -0700947 drivers = [ None, "Bench", "CLI", "REST", "server" ]
Jon Hallca319892017-06-15 15:25:22 -0700948 results = []
Jon Hall3e6edb32018-08-21 16:20:30 -0700949 for ctrl in self.fromNode( getFrom ):
Jon Hall3c0114c2020-08-11 15:07:42 -0700950 funcArgs = []
951 funcKwargs = {}
Devin Lim142b5342017-07-20 15:22:39 -0700952 try:
953 f = getattr( ( ctrl if not specificDriver else
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700954 getattr( ctrl, drivers[ specificDriver ] ) ), function )
Devin Lim142b5342017-07-20 15:22:39 -0700955 except AttributeError:
956 main.log.error( "Function " + function + " not found. Exiting the Test." )
Devin Lim44075962017-08-11 10:56:37 -0700957 main.cleanAndExit()
Jon Hall3c0114c2020-08-11 15:07:42 -0700958 if funcFromCtrl:
959 if args:
960 try:
961 for i in range( len( args ) ):
962 funcArgs.append( getattr( ctrl, args[ i ] ) )
963 except AttributeError:
964 main.log.error( "Argument " + str( args[ i ] ) + " for " + str( f ) + " not found. Exiting the Test." )
965 main.cleanAndExit()
966 if kwargs:
967 try:
968 for k in kwargs:
969 funcKwargs.update( { k: getattr( ctrl, kwargs[ k ] ) } )
970 except AttributeError as e:
971 main.log.exception("")
972 main.log.error( "Keyword Argument " + str( k ) + " for " + str( f ) + " not found. Exiting the Test." )
973 main.log.debug( "Passed kwargs: %s; dir(ctrl): %s" % ( repr( kwargs ), dir( ctrl ) ) )
974 main.cleanAndExit()
Jon Hallca319892017-06-15 15:25:22 -0700975 t = main.Thread( target=f,
976 name=function + "-" + ctrl.name,
Devin Lim40a19092017-08-15 14:54:22 -0700977 args=funcArgs if funcFromCtrl else args,
978 kwargs=funcKwargs if funcFromCtrl else kwargs )
Jon Hallca319892017-06-15 15:25:22 -0700979 threads.append( t )
980 t.start()
981
982 for t in threads:
983 t.join()
984 results.append( t.result )
Devin Lim142b5342017-07-20 15:22:39 -0700985 if returnBool:
Jon Hall3e6edb32018-08-21 16:20:30 -0700986 return self.allTrueResultCheck( results, self.fromNode( getFrom ) )
Devin Lim142b5342017-07-20 15:22:39 -0700987 elif contentCheck:
Jon Hall3e6edb32018-08-21 16:20:30 -0700988 return self.notEmptyResultCheck( results, self.fromNode( getFrom ) )
Jon Hall4173b242017-09-12 17:04:38 -0700989 return results
990
991 def checkPartitionSize( self, segmentSize='64', units='M', multiplier='3' ):
992 # max segment size in bytes: 1024 * 1024 * 64
993 # multiplier is somewhat arbitrary, but the idea is the logs would have
994 # been compacted before this many segments are written
995
Jon Hall43060f62020-06-23 13:13:33 -0700996 try:
997 maxSize = float( segmentSize ) * float( multiplier )
998 ret = True
999 for n in self.runningNodes:
1000 # Partition logs
1001 ret = ret and n.server.folderSize( "/opt/atomix/data/raft/partitions/*/*.log",
1002 size=maxSize, unit=units, ignoreRoot=False )
1003 return ret
1004 except Exception as e:
1005 main.log.error( e )
1006 main.log.error( repr( e ) )