blob: e5433f9d2a0a24dfc19a1ae27478e45dfc41945c [file] [log] [blame]
Jeremy Ronquillob27ce4c2017-07-17 12:41:28 -07001"""
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07002Copyright 2016 Open Networking Foundation ( ONF )
Jeremy Ronquillob27ce4c2017-07-17 12:41:28 -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.
Jeremy Ronquillob27ce4c2017-07-17 12:41:28 -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"""
Jon Hall1efcb3f2016-08-23 13:42:15 -070021import os
Jon Hall1efcb3f2016-08-23 13:42:15 -070022import time
23import json
24import urllib
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -070025import re
Jon Hall1efcb3f2016-08-23 13:42:15 -070026from core import utilities
27
28
29class Testcaselib:
Pierfb719b12016-09-19 14:51:44 -070030
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070031 useSSH = True
Pierfb719b12016-09-19 14:51:44 -070032
Jon Hall1efcb3f2016-08-23 13:42:15 -070033 @staticmethod
34 def initTest( main ):
35 """
36 - Construct tests variables
37 - GIT ( optional )
38 - Checkout ONOS master branch
39 - Pull latest ONOS code
40 - Building ONOS ( optional )
41 - Install ONOS package
42 - Build ONOS package
43 """
Devin Lim58046fa2017-07-05 16:55:00 -070044 try:
45 from tests.dependencies.ONOSSetup import ONOSSetup
46 main.testSetUp = ONOSSetup()
47 except ImportError:
48 main.log.error( "ONOSSetup not found. exiting the test" )
Devin Lim44075962017-08-11 10:56:37 -070049 main.cleanAndExit()
You Wangd5873482018-01-24 12:30:00 -080050 from tests.dependencies.Network import Network
51 main.Network = Network()
Devin Lim0c972b72018-02-08 14:53:59 -080052 main.testSetUp.envSetupDescription( False )
Devin Lim58046fa2017-07-05 16:55:00 -070053 stepResult = main.FALSE
54 try:
Devin Lim58046fa2017-07-05 16:55:00 -070055 # Test variables
56 main.cellName = main.params[ 'ENV' ][ 'cellName' ]
57 main.apps = main.params[ 'ENV' ][ 'cellApps' ]
Devin Lim58046fa2017-07-05 16:55:00 -070058 main.path = os.path.dirname( main.testFile )
Devin Lim57221b02018-02-14 15:45:36 -080059 main.useCommonTopo = main.params[ 'DEPENDENCY' ][ 'useCommonTopo' ] == 'True'
60 main.topoPath = main.path + ( "/.." if main.useCommonTopo else "" ) + "/dependencies/"
61 main.useCommonConf = main.params[ 'DEPENDENCY' ][ 'useCommonConf' ] == 'True'
62 main.configPath = main.path + ( "/.." if main.useCommonConf else "" ) + "/dependencies/"
63 main.forJson = "json/"
64 main.forChart = "chart/"
65 main.forConfig = "conf/"
66 main.forHost = "host/"
You Wang27317572018-03-06 12:13:11 -080067 main.forSwitchFailure = "switchFailure/"
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -080068 main.forLinkFailure = "linkFailure/"
You Wange24d6272018-03-27 21:18:50 -070069 main.forMulticast = "multicast/"
Devin Lim58046fa2017-07-05 16:55:00 -070070 main.topology = main.params[ 'DEPENDENCY' ][ 'topology' ]
You Wangd87b2312018-01-30 12:47:17 -080071 main.topologyLib = main.params[ 'DEPENDENCY' ][ 'lib' ] if 'lib' in main.params[ 'DEPENDENCY' ] else None
72 main.topologyConf = main.params[ 'DEPENDENCY' ][ 'conf' ] if 'conf' in main.params[ 'DEPENDENCY' ] else None
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070073 main.scale = ( main.params[ 'SCALE' ][ 'size' ] ).split( "," )
Devin Lim58046fa2017-07-05 16:55:00 -070074 main.maxNodes = int( main.params[ 'SCALE' ][ 'max' ] )
Jon Hall1efcb3f2016-08-23 13:42:15 -070075
Devin Lim0c972b72018-02-08 14:53:59 -080076 stepResult = main.testSetUp.envSetup( False )
Devin Lim58046fa2017-07-05 16:55:00 -070077 except Exception as e:
78 main.testSetUp.envSetupException( e )
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -070079
Devin Lim58046fa2017-07-05 16:55:00 -070080 main.testSetUp.evnSetupConclusion( stepResult )
Jon Hall1efcb3f2016-08-23 13:42:15 -070081
Jon Hall1efcb3f2016-08-23 13:42:15 -070082 @staticmethod
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -080083 def installOnos( main, vlanCfg=True, skipPackage=False, cliSleep=10,
84 parallel=True ):
Jon Hall1efcb3f2016-08-23 13:42:15 -070085 """
86 - Set up cell
87 - Create cell file
88 - Set cell file
89 - Verify cell file
90 - Kill ONOS process
91 - Uninstall ONOS cluster
92 - Verify ONOS start up
93 - Install ONOS cluster
94 - Connect to cli
95 """
96 # main.scale[ 0 ] determines the current number of ONOS controller
You Wangd87b2312018-01-30 12:47:17 -080097 if not main.apps:
Jon Hall1efcb3f2016-08-23 13:42:15 -070098 main.log.error( "App list is empty" )
Jon Hall3c910162018-03-07 14:42:16 -080099 main.log.info( "Cluster size: " + str( main.Cluster.numCtrls ) )
100 main.log.info( "Cluster ips: " + ', '.join( main.Cluster.getIps() ) )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700101 main.dynamicHosts = [ 'in1', 'out1' ]
You Wanga0f6ff62018-01-11 15:46:30 -0800102 main.testSetUp.ONOSSetUp( main.Cluster, newCell=True, cellName=main.cellName,
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -0800103 skipPack=skipPackage,
104 useSSH=Testcaselib.useSSH,
Devin Lim0c972b72018-02-08 14:53:59 -0800105 installParallel=parallel, includeCaseDesc=False )
Devin Lim142b5342017-07-20 15:22:39 -0700106 ready = utilities.retry( main.Cluster.active( 0 ).CLI.summary,
107 main.FALSE,
You Wang1cdc5f52017-12-19 16:47:51 -0800108 sleep=cliSleep,
Devin Lim142b5342017-07-20 15:22:39 -0700109 attempts=10 )
110 if ready:
111 ready = main.TRUE
112 utilities.assert_equals( expect=main.TRUE, actual=ready,
Jon Hall1efcb3f2016-08-23 13:42:15 -0700113 onpass="ONOS summary command succeded",
114 onfail="ONOS summary command failed" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700115 if not ready:
116 main.log.error( "ONOS startup failed!" )
Devin Lim44075962017-08-11 10:56:37 -0700117 main.cleanAndExit()
Jon Hall1efcb3f2016-08-23 13:42:15 -0700118
Devin Lim142b5342017-07-20 15:22:39 -0700119 for ctrl in main.Cluster.active():
120 ctrl.CLI.logSet( "DEBUG", "org.onosproject.segmentrouting" )
You Wangf5f104f2018-03-30 17:09:10 -0700121 ctrl.CLI.logSet( "DEBUG", "org.onosproject.driver" )
Devin Lim142b5342017-07-20 15:22:39 -0700122 ctrl.CLI.logSet( "DEBUG", "org.onosproject.net.flowobjective.impl" )
You Wangf5f104f2018-03-30 17:09:10 -0700123 ctrl.CLI.logSet( "DEBUG", "org.onosproject.routeservice.impl" )
124 ctrl.CLI.logSet( "DEBUG", "org.onosproject.routeservice.store" )
125 ctrl.CLI.logSet( "DEBUG", "org.onosproject.routing.fpm" )
Jon Hall9677ed32018-04-24 11:16:23 -0700126 ctrl.CLI.logSet( "TRACE", "org.onosproject.events" )
127 ctrl.CLI.logSet( "DEBUG", "org.onosproject.mcast" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700128
129 @staticmethod
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800130 def loadCount( main ):
131 with open("%s/count/%s.count" % (main.configPath, main.cfgName)) as count:
You Wang5df1c6d2018-04-06 18:02:02 -0700132 main.count = json.load(count)
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800133
134 @staticmethod
Devin Lim57221b02018-02-14 15:45:36 -0800135 def loadJson( main ):
136 with open( "%s%s.json" % ( main.configPath + main.forJson,
137 main.cfgName ) ) as cfg:
138 main.Cluster.active( 0 ).REST.setNetCfg( json.load( cfg ) )
139
140 @staticmethod
141 def loadChart( main ):
142 try:
143 with open( "%s%s.chart" % ( main.configPath + main.forChart,
144 main.cfgName ) ) as chart:
145 main.pingChart = json.load(chart)
146 except IOError:
147 main.log.warn( "No chart file found." )
148
149 @staticmethod
150 def loadHost( main ):
151 with open( "%s%s.host" % ( main.configPath + main.forHost,
152 main.cfgName ) ) as host:
153 main.expectedHosts = json.load( host )
154
155 @staticmethod
You Wang27317572018-03-06 12:13:11 -0800156 def loadSwitchFailureChart( main ):
157 with open( "%s%s.switchFailureChart" % ( main.configPath + main.forSwitchFailure,
158 main.cfgName ) ) as sfc:
159 main.switchFailureChart = json.load( sfc )
160
161 @staticmethod
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800162 def loadLinkFailureChart( main ):
163 with open( "%s%s.linkFailureChart" % ( main.configPath + main.forLinkFailure,
You Wange24d6272018-03-27 21:18:50 -0700164 main.cfgName ) ) as lfc:
165 main.linkFailureChart = json.load( lfc )
166
167 @staticmethod
168 def loadMulticastConfig( main ):
169 with open( "%s%s.multicastConfig" % ( main.configPath + main.forMulticast,
170 main.cfgName ) ) as cfg:
171 main.multicastConfig = json.load( cfg )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800172
173 @staticmethod
Jon Hall1efcb3f2016-08-23 13:42:15 -0700174 def startMininet( main, topology, args="" ):
You Wangd87b2312018-01-30 12:47:17 -0800175 copyResult = main.ONOSbench.scp( main.Mininet1,
176 main.topoPath + main.topology,
You Wang5da39c82018-04-26 22:55:08 -0700177 main.Mininet1.home + "custom",
You Wangd87b2312018-01-30 12:47:17 -0800178 direction="to" )
179 if main.topologyLib:
180 for lib in main.topologyLib.split(","):
181 copyResult = copyResult and main.ONOSbench.scp( main.Mininet1,
182 main.topoPath + lib,
You Wang5da39c82018-04-26 22:55:08 -0700183 main.Mininet1.home + "custom",
You Wangd87b2312018-01-30 12:47:17 -0800184 direction="to" )
185 if main.topologyConf:
You Wanga877ea42018-04-05 15:27:40 -0700186 import re
187 controllerIPs = [ ctrl.ipAddress for ctrl in main.Cluster.runningNodes ]
188 index = 0
You Wangd87b2312018-01-30 12:47:17 -0800189 for conf in main.topologyConf.split(","):
You Wanga877ea42018-04-05 15:27:40 -0700190 # Update zebra configurations with correct ONOS instance IP
191 if conf in [ "zebradbgp1.conf", "zebradbgp2.conf" ]:
192 ip = controllerIPs[ index ]
193 index = ( index + 1 ) % len( controllerIPs )
194 with open( main.configPath + main.forConfig + conf ) as f:
195 s = f.read()
196 s = re.sub( r"(fpm connection ip).*(port 2620)", r"\1 " + ip + r" \2", s )
197 with open( main.configPath + main.forConfig + conf, "w" ) as f:
198 f.write( s )
You Wangd87b2312018-01-30 12:47:17 -0800199 copyResult = copyResult and main.ONOSbench.scp( main.Mininet1,
Devin Lim57221b02018-02-14 15:45:36 -0800200 main.configPath + main.forConfig + conf,
You Wangd87b2312018-01-30 12:47:17 -0800201 "~/",
202 direction="to" )
203 stepResult = copyResult
204 utilities.assert_equals( expect=main.TRUE,
205 actual=stepResult,
206 onpass="Successfully copied topo files",
207 onfail="Failed to copy topo files" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700208 main.step( "Starting Mininet Topology" )
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700209 arg = "--onos-ip=%s %s" % (",".join([ctrl.ipAddress for ctrl in main.Cluster.runningNodes]), args)
Jon Hall1efcb3f2016-08-23 13:42:15 -0700210 main.topology = topology
211 topoResult = main.Mininet1.startNet(
You Wang5da39c82018-04-26 22:55:08 -0700212 topoFile=main.Mininet1.home + "custom/" + main.topology, args=arg )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700213 stepResult = topoResult
214 utilities.assert_equals( expect=main.TRUE,
215 actual=stepResult,
216 onpass="Successfully loaded topology",
217 onfail="Failed to load topology" )
218 # Exit if topology did not load properly
219 if not topoResult:
Devin Lim44075962017-08-11 10:56:37 -0700220 main.cleanAndExit()
Jon Hall1efcb3f2016-08-23 13:42:15 -0700221
222 @staticmethod
You Wang4cc61912018-08-28 10:10:58 -0700223 def connectToPhysicalNetwork( main ):
You Wang84f981d2018-01-12 16:11:50 -0800224 main.step( "Connecting to physical netowrk" )
225 topoResult = main.NetworkBench.connectToNet()
226 stepResult = topoResult
227 utilities.assert_equals( expect=main.TRUE,
228 actual=stepResult,
229 onpass="Successfully loaded topology",
230 onfail="Failed to load topology" )
231 # Exit if topology did not load properly
232 if not topoResult:
233 main.cleanAndExit()
234
235 main.step( "Assign switches to controllers." )
236 assignResult = main.TRUE
You Wang4cc61912018-08-28 10:10:58 -0700237 switches = main.NetworkBench.getSwitches()
238 pool = []
239 for name in switches.keys():
240 thread = main.Thread( target=main.NetworkBench.assignSwController,
241 name="assignSwitchToController",
242 args=[ name, main.Cluster.getIps(), '6653' ] )
243 pool.append( thread )
244 thread.start()
245 for thread in pool:
246 thread.join( 300 )
247 if not thread.result:
248 stepResult = main.FALSE
You Wang84f981d2018-01-12 16:11:50 -0800249 utilities.assert_equals( expect=main.TRUE,
250 actual=stepResult,
251 onpass="Successfully assign switches to controllers",
252 onfail="Failed to assign switches to controllers" )
253
You Wang4cc61912018-08-28 10:10:58 -0700254 # Check devices
255 Testcaselib.checkDevices( main, switches=int( main.params[ 'TOPO' ][ 'switchNum' ] ) )
256 time.sleep( float( main.params[ "timers" ][ "connectToNetSleep" ] ) )
257 # Connecting to hosts that only have data plane connectivity
258 main.step( "Connecting inband hosts" )
259 stepResult = main.Network.connectInbandHosts()
260 utilities.assert_equals( expect=main.TRUE,
261 actual=stepResult,
262 onpass="Successfully connected inband hosts",
263 onfail="Failed to connect inband hosts" )
264
You Wang84f981d2018-01-12 16:11:50 -0800265 @staticmethod
You Wang5df1c6d2018-04-06 18:02:02 -0700266 def saveOnosDiagnostics( main ):
267 """
268 Get onos-diags.tar.gz and save it to the log directory.
269 suffix: suffix string of the file name. E.g. onos-diags-case1.tar.gz
270 """
271 main.log.info( "Collecting onos-diags..." )
272 main.ONOSbench.onosDiagnostics( [ctrl.ipAddress for ctrl in main.Cluster.runningNodes],
273 main.logdir,
274 "-CASE%d" % main.CurrentTestCaseNumber )
275
276 @staticmethod
Devin Lim142b5342017-07-20 15:22:39 -0700277 def config( main, cfgName ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700278 main.spines = []
Piera2a7e1b2016-10-04 11:51:43 -0700279
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700280 main.failures = int( main.params[ 'failures' ] )
281 main.cfgName = cfgName
Piera2a7e1b2016-10-04 11:51:43 -0700282
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700283 if main.cfgName == '2x2':
284 spine = {}
285 spine[ 'name' ] = main.params[ 'switches' ][ 'spine1' ]
286 spine[ 'dpid' ] = main.params[ 'switches' ][ 'spinedpid1' ]
287 main.spines.append( spine )
Piera2a7e1b2016-10-04 11:51:43 -0700288
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700289 spine = {}
290 spine[ 'name' ] = main.params[ 'switches' ][ 'spine2' ]
291 spine[ 'dpid' ] = main.params[ 'switches' ][ 'spinedpid2' ]
292 main.spines.append( spine )
Piera2a7e1b2016-10-04 11:51:43 -0700293
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700294 elif main.cfgName == '4x4':
295 spine = {}
296 spine[ 'name' ] = main.params[ 'switches' ][ 'spine1' ]
297 spine[ 'dpid' ] = main.params[ 'switches' ][ 'spinedpid1' ]
298 main.spines.append( spine )
Piera2a7e1b2016-10-04 11:51:43 -0700299
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700300 spine = {}
301 spine[ 'name' ] = main.params[ 'switches' ][ 'spine2' ]
302 spine[ 'dpid' ] = main.params[ 'switches' ][ 'spinedpid2' ]
303 main.spines.append( spine )
Piera2a7e1b2016-10-04 11:51:43 -0700304
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700305 spine = {}
306 spine[ 'name' ] = main.params[ 'switches' ][ 'spine3' ]
307 spine[ 'dpid' ] = main.params[ 'switches' ][ 'spinedpid3' ]
308 main.spines.append( spine )
Piera2a7e1b2016-10-04 11:51:43 -0700309
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700310 spine = {}
311 spine[ 'name' ] = main.params[ 'switches' ][ 'spine4' ]
312 spine[ 'dpid' ] = main.params[ 'switches' ][ 'spinedpid4' ]
313 main.spines.append( spine )
Piera2a7e1b2016-10-04 11:51:43 -0700314
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700315 else:
Piera2a7e1b2016-10-04 11:51:43 -0700316 main.log.error( "Configuration failed!" )
Devin Lim44075962017-08-11 10:56:37 -0700317 main.cleanAndExit()
You Wang27317572018-03-06 12:13:11 -0800318
Andreas Pantelopoulos2eae3242018-03-06 13:47:20 -0800319 @staticmethod
320 def addStaticOnosRoute( main, subnet, intf):
321 """
322 Adds an ONOS static route with the use route-add command.
323 """
Andreas Pantelopoulos2eae3242018-03-06 13:47:20 -0800324 routeResult = main.Cluster.active( 0 ).addStaticRoute(subnet, intf)
325
Piera2a7e1b2016-10-04 11:51:43 -0700326 @staticmethod
You Wangc02f3be2018-05-18 12:14:23 -0700327 def checkGroupsForBuckets( main, deviceId, subnetDict, routingTable=30 ):
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700328 """
329 Check number of groups for each subnet on device deviceId and matches
330 it with an expected value. subnetDict is a dictionarty containing values
331 of the type "10.0.1.0/24" : 5.
332 """
You Wangc02f3be2018-05-18 12:14:23 -0700333 main.step( "Checking if number of groups for subnets in device {0} is as expected.".format( deviceId ) )
334 groups = main.Cluster.active( 0 ).CLI.getGroups( deviceId, groupType="select" )
335 flows = main.Cluster.active( 0 ).CLI.flows( jsonFormat=False, device=deviceId )
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700336
You Wangc02f3be2018-05-18 12:14:23 -0700337 result = main.TRUE
338 for subnet, numberInSelect in subnetDict.iteritems():
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700339 for flow in flows.splitlines():
You Wangc02f3be2018-05-18 12:14:23 -0700340 if "tableId={0}".format( routingTable ) in flow and subnet in flow:
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700341 # this will match the group id that this flow entry points to, for example :
342 # 0x70000041 in flow entry which contains "deferred=[GROUP:0x70000041], transition=TABLE:60,"
You Wangc02f3be2018-05-18 12:14:23 -0700343 groupId = re.search( r".*GROUP:(0x.*)], transition.*", flow ).groups()[0]
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700344 count = 0
345 for group in groups.splitlines():
You Wangc02f3be2018-05-18 12:14:23 -0700346 if 'id={0}'.format( groupId ) in group:
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700347 count += 1
You Wangc02f3be2018-05-18 12:14:23 -0700348 if count - 1 != numberInSelect:
349 result = main.FALSE
350 main.log.warn( "Mismatch in number of buckets of select group, found {0}, expected {1} for subnet {2} on device {3}".format( count - 1, numberInSelect, subnet, deviceId ) )
351 utilities.assert_equals( expect=main.TRUE, actual=result,
352 onpass="All bucket numbers are as expected",
353 onfail="Some bucket numbers are not as expected" )
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700354
355 @staticmethod
Jonghwan Hyun76a02b72018-01-30 16:40:48 +0900356 def checkFlows( main, minFlowCount, tag="", dumpflows=True, sleep=10 ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700357 main.step(
Jon Hall3c910162018-03-07 14:42:16 -0800358 "Check whether the flow count is bigger than %s" % minFlowCount )
Jonghwan Hyun76a02b72018-01-30 16:40:48 +0900359 if tag == "":
360 tag = 'CASE%d' % main.CurrentTestCaseNumber
Devin Lim142b5342017-07-20 15:22:39 -0700361 count = utilities.retry( main.Cluster.active( 0 ).CLI.checkFlowCount,
Jon Hall1efcb3f2016-08-23 13:42:15 -0700362 main.FALSE,
363 kwargs={ 'min': minFlowCount },
364 attempts=10,
You Wang1cdc5f52017-12-19 16:47:51 -0800365 sleep=sleep )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700366 utilities.assertEquals(
Jon Hall1efcb3f2016-08-23 13:42:15 -0700367 expect=True,
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700368 actual=( count > 0 ),
Jon Hall1efcb3f2016-08-23 13:42:15 -0700369 onpass="Flow count looks correct: " + str( count ),
370 onfail="Flow count looks wrong: " + str( count ) )
371
372 main.step( "Check whether all flow status are ADDED" )
Devin Lim142b5342017-07-20 15:22:39 -0700373 flowCheck = utilities.retry( main.Cluster.active( 0 ).CLI.checkFlowsState,
Jon Hall1efcb3f2016-08-23 13:42:15 -0700374 main.FALSE,
375 kwargs={ 'isPENDING': False },
Jonghwan Hyun98fb40a2018-01-04 16:16:28 -0800376 attempts=5,
You Wang1cdc5f52017-12-19 16:47:51 -0800377 sleep=sleep )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700378 utilities.assertEquals(
Jon Hall1efcb3f2016-08-23 13:42:15 -0700379 expect=main.TRUE,
380 actual=flowCheck,
381 onpass="Flow status is correct!",
382 onfail="Flow status is wrong!" )
383 if dumpflows:
Devin Lim142b5342017-07-20 15:22:39 -0700384 main.ONOSbench.dumpONOSCmd( main.Cluster.active( 0 ).ipAddress,
Pier50f0bc62016-09-07 17:53:40 -0700385 "flows",
386 main.logdir,
Jonghwan Hyun76a02b72018-01-30 16:40:48 +0900387 tag + "_FlowsBefore" )
Devin Lim142b5342017-07-20 15:22:39 -0700388 main.ONOSbench.dumpONOSCmd( main.Cluster.active( 0 ).ipAddress,
Pier50f0bc62016-09-07 17:53:40 -0700389 "groups",
390 main.logdir,
Jonghwan Hyun76a02b72018-01-30 16:40:48 +0900391 tag + "_GroupsBefore" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700392
393 @staticmethod
Pier6a0c4de2018-03-18 16:01:30 -0700394 def checkDevices( main, switches, tag="", sleep=10 ):
395 main.step(
396 "Check whether the switches count is equal to %s" % switches )
397 if tag == "":
398 tag = 'CASE%d' % main.CurrentTestCaseNumber
399 result = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
400 main.FALSE,
401 kwargs={ 'numoswitch': switches},
402 attempts=10,
403 sleep=sleep )
404 utilities.assert_equals( expect=main.TRUE, actual=result,
405 onpass="Device up successful",
406 onfail="Failed to boot up devices?" )
407
408 @staticmethod
Jonghwan Hyun98fb40a2018-01-04 16:16:28 -0800409 def checkFlowsByDpid( main, dpid, minFlowCount, sleep=10 ):
410 main.step(
Jonghwan Hyuncf2345c2018-02-26 11:07:54 -0800411 " Check whether the flow count of device %s is bigger than %s" % ( dpid, minFlowCount ) )
412 count = utilities.retry( main.Cluster.active( 0 ).CLI.checkFlowAddedCount,
413 main.FALSE,
414 args=( dpid, minFlowCount ),
Jonghwan Hyun98fb40a2018-01-04 16:16:28 -0800415 attempts=5,
416 sleep=sleep )
417 utilities.assertEquals(
Jonghwan Hyuncf2345c2018-02-26 11:07:54 -0800418 expect=True,
419 actual=( count > minFlowCount ),
420 onpass="Flow count looks correct: " + str( count ),
421 onfail="Flow count looks wrong. " )
Jonghwan Hyun98fb40a2018-01-04 16:16:28 -0800422
423 @staticmethod
Jon Hall9677ed32018-04-24 11:16:23 -0700424 def checkFlowEqualityByDpid( main, dpid, flowCount, sleep=10 ):
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800425 main.step(
426 " Check whether the flow count of device %s is equal to %s" % ( dpid, flowCount ) )
427 count = utilities.retry( main.Cluster.active( 0 ).CLI.checkFlowAddedCount,
428 main.FALSE,
Jon Hall9677ed32018-04-24 11:16:23 -0700429 args=( dpid, flowCount, False, 1 ),
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800430 attempts=5,
431 sleep=sleep )
432
433 utilities.assertEquals(
434 expect=True,
435 actual=( int( count ) == flowCount ),
Jon Hall9677ed32018-04-24 11:16:23 -0700436 onpass="Flow count looks correct: " + str( count ) ,
437 onfail="Flow count looks wrong. found {}, should be {}.".format( count, flowCount ) )
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800438
439 @staticmethod
440 def checkGroupEqualityByDpid( main, dpid, groupCount, sleep=10):
441 main.step(
442 " Check whether the group count of device %s is equal to %s" % ( dpid, groupCount ) )
443 count = utilities.retry( main.Cluster.active( 0 ).CLI.checkGroupAddedCount,
444 main.FALSE,
445 args=( dpid, groupCount, False, 1),
446 attempts=5,
447 sleep=sleep )
448
449 utilities.assertEquals(
450 expect=True,
451 actual=( count == groupCount ),
Jon Hall9677ed32018-04-24 11:16:23 -0700452 onpass="Group count looks correct: " + str( count ) ,
453 onfail="Group count looks wrong. found {}, should be {}.".format( count, groupCount ) )
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800454
455 @staticmethod
Jon Hall9677ed32018-04-24 11:16:23 -0700456 def checkFlowsGroupsFromFile( main ):
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800457
458 for dpid, values in main.count.items():
459 flowCount = values["flows"]
460 groupCount = values["groups"]
Jon Hall9677ed32018-04-24 11:16:23 -0700461 main.log.report( "Check flow count for dpid " + str( dpid ) +
462 ", should be " + str( flowCount ) )
463 Testcaselib.checkFlowEqualityByDpid( main, dpid, flowCount )
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800464
Jon Hall9677ed32018-04-24 11:16:23 -0700465 main.log.report( "Check group count for dpid " + str( dpid ) +
466 ", should be " + str( groupCount ) )
467 Testcaselib.checkGroupEqualityByDpid( main, dpid, groupCount )
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800468
469 return
470
471 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700472 def pingAll( main, tag="", dumpflows=True, acceptableFailed=0, basedOnIp=False,
473 sleep=10, retryAttempts=1, skipOnFail=False ):
You Wangf19d9f42018-02-23 16:34:19 -0800474 '''
You Wangba231e72018-03-01 13:18:21 -0800475 Verify connectivity between hosts according to the ping chart
476 acceptableFailed: max number of acceptable failed pings.
You Wangf19d9f42018-02-23 16:34:19 -0800477 basedOnIp: if True, run ping or ping6 based on suffix of host names
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800478 retryAttempts: the number of retry ping. Only works for IPv4 hosts.
You Wangf19d9f42018-02-23 16:34:19 -0800479 '''
You Wangba231e72018-03-01 13:18:21 -0800480 main.log.report( "Check host connectivity" )
481 main.log.debug( "Ping chart: %s" % main.pingChart )
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800482 if tag == "":
483 tag = 'CASE%d' % main.CurrentTestCaseNumber
484 for entry in main.pingChart.itervalues():
You Wangba231e72018-03-01 13:18:21 -0800485 main.log.debug( "Entry in ping chart: %s" % entry )
486 expect = entry[ 'expect' ]
487 if expect == "Unidirectional":
488 # Verify ping from each src host to each dst host
489 src = entry[ 'src' ]
490 dst = entry[ 'dst' ]
491 expect = main.TRUE
492 main.step( "Verify unidirectional connectivity from %s to %s with tag %s" % ( str( src ), str( dst ), tag ) )
493 if basedOnIp:
494 if ("v4" in src[0]):
495 pa = main.Network.pingallHostsUnidirectional( src, dst, acceptableFailed=acceptableFailed )
496 utilities.assert_equals( expect=expect, actual=pa,
497 onpass="IPv4 connectivity successfully tested",
498 onfail="IPv4 connectivity failed" )
499 if ("v6" in src[0]):
500 pa = main.Network.pingallHostsUnidirectional( src, dst, ipv6=True, acceptableFailed=acceptableFailed )
501 utilities.assert_equals( expect=expect, actual=pa,
502 onpass="IPv6 connectivity successfully tested",
503 onfail="IPv6 connectivity failed" )
504 else:
505 pa = main.Network.pingallHostsUnidirectional( src, dst, acceptableFailed=acceptableFailed )
506 utilities.assert_equals( expect=expect, actual=pa,
507 onpass="IP connectivity successfully tested",
508 onfail="IP connectivity failed" )
509 else:
510 # Verify ping between each host pair
511 hosts = entry[ 'hosts' ]
512 try:
513 expect = main.TRUE if str(expect).lower() == 'true' else main.FALSE
514 except:
515 expect = main.FALSE
516 main.step( "Verify full connectivity for %s with tag %s" % ( str( hosts ), tag ) )
517 if basedOnIp:
518 if ("v4" in hosts[0]):
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800519 pa = utilities.retry( main.Network.pingallHosts,
520 main.FALSE if expect else main.TRUE,
521 args=(hosts,),
522 attempts=retryAttempts,
523 sleep=sleep )
You Wangba231e72018-03-01 13:18:21 -0800524 utilities.assert_equals( expect=expect, actual=pa,
525 onpass="IPv4 connectivity successfully tested",
526 onfail="IPv4 connectivity failed" )
527 if ("v6" in hosts[0]):
528 pa = main.Network.pingIpv6Hosts( hosts, acceptableFailed=acceptableFailed )
529 utilities.assert_equals( expect=expect, actual=pa,
530 onpass="IPv6 connectivity successfully tested",
531 onfail="IPv6 connectivity failed" )
532 else:
You Wangf19d9f42018-02-23 16:34:19 -0800533 pa = main.Network.pingallHosts( hosts )
534 utilities.assert_equals( expect=expect, actual=pa,
You Wangba231e72018-03-01 13:18:21 -0800535 onpass="IP connectivity successfully tested",
536 onfail="IP connectivity failed" )
You Wang5df1c6d2018-04-06 18:02:02 -0700537 if skipOnFail and pa != expect:
538 Testcaselib.saveOnosDiagnostics( main )
You Wang24ad2f52018-04-10 10:47:12 -0700539 Testcaselib.cleanup( main, copyKarafLog=False )
You Wang5df1c6d2018-04-06 18:02:02 -0700540 main.skipCase()
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800541
542 if dumpflows:
543 main.ONOSbench.dumpONOSCmd( main.Cluster.active( 0 ).ipAddress,
544 "flows",
545 main.logdir,
546 tag + "_FlowsOn" )
547 main.ONOSbench.dumpONOSCmd( main.Cluster.active( 0 ).ipAddress,
548 "groups",
549 main.logdir,
550 tag + "_GroupsOn" )
551
552 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700553 def killLink( main, end1, end2, switches, links, sleep=None ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700554 """
555 end1,end2: identify the switches, ex.: 'leaf1', 'spine1'
556 switches, links: number of expected switches and links after linkDown, ex.: '4', '6'
557 Kill a link and verify ONOS can see the proper link change
558 """
Jon Halla604fd42018-05-04 14:27:27 -0700559 if sleep is None:
560 sleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
561 else:
562 sleep = float( sleep )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700563 main.step( "Kill link between %s and %s" % ( end1, end2 ) )
Jon Halla604fd42018-05-04 14:27:27 -0700564 linkDown = main.Network.link( END1=end1, END2=end2, OPTION="down" )
565 linkDown = linkDown and main.Network.link( END2=end1, END1=end2, OPTION="down" )
566 # TODO: Can remove this, since in the retry we will wait anyways if topology is incorrect
Jon Hall1efcb3f2016-08-23 13:42:15 -0700567 main.log.info(
Jon Halla604fd42018-05-04 14:27:27 -0700568 "Waiting %s seconds for link down to be discovered" % sleep )
569 time.sleep( sleep )
Devin Lim142b5342017-07-20 15:22:39 -0700570 topology = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
Jon Hall1efcb3f2016-08-23 13:42:15 -0700571 main.FALSE,
572 kwargs={ 'numoswitch': switches,
573 'numolink': links },
574 attempts=10,
Jon Halla604fd42018-05-04 14:27:27 -0700575 sleep=sleep )
576 result = topology and linkDown
Jon Hall1efcb3f2016-08-23 13:42:15 -0700577 utilities.assert_equals( expect=main.TRUE, actual=result,
578 onpass="Link down successful",
579 onfail="Failed to turn off link?" )
580
581 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700582 def killLinkBatch( main, links, linksAfter, switches, sleep=None ):
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800583 """
584 links = list of links (src, dst) to bring down.
585 """
586
587 main.step("Killing a batch of links {0}".format(links))
Jon Halla604fd42018-05-04 14:27:27 -0700588 if sleep is None:
589 sleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
590 else:
591 sleep = float( sleep )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800592
593 for end1, end2 in links:
594 main.Network.link( END1=end1, END2=end2, OPTION="down")
595 main.Network.link( END1=end2, END2=end1, OPTION="down")
596
Jon Halla604fd42018-05-04 14:27:27 -0700597 # TODO: Can remove this, since in the retry we will wait anyways if topology is incorrect
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800598 main.log.info(
Jon Halla604fd42018-05-04 14:27:27 -0700599 "Waiting %s seconds for links down to be discovered" % sleep )
600 time.sleep( sleep )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800601
602 topology = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
603 main.FALSE,
604 kwargs={ 'numoswitch': switches,
605 'numolink': linksAfter },
606 attempts=10,
Jon Halla604fd42018-05-04 14:27:27 -0700607 sleep=sleep )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800608
You Wang2854bce2018-03-30 10:15:32 -0700609 utilities.assert_equals( expect=main.TRUE, actual=topology,
610 onpass="Link batch down successful",
611 onfail="Link batch down failed" )
612
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800613 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700614 def restoreLinkBatch( main, links, linksAfter, switches, sleep=None ):
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800615 """
616 links = list of link (src, dst) to bring up again.
617 """
618
619 main.step("Restoring a batch of links {0}".format(links))
Jon Halla604fd42018-05-04 14:27:27 -0700620 if sleep is None:
621 sleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
622 else:
623 sleep = float( sleep )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800624
625 for end1, end2 in links:
626 main.Network.link( END1=end1, END2=end2, OPTION="up")
627 main.Network.link( END1=end2, END2=end1, OPTION="up")
628
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800629 main.log.info(
Jon Halla604fd42018-05-04 14:27:27 -0700630 "Waiting %s seconds for links up to be discovered" % sleep )
631 time.sleep( sleep )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800632
633 topology = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
634 main.FALSE,
635 kwargs={ 'numoswitch': switches,
636 'numolink': linksAfter },
637 attempts=10,
Jon Halla604fd42018-05-04 14:27:27 -0700638 sleep=sleep )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800639
You Wang2854bce2018-03-30 10:15:32 -0700640 utilities.assert_equals( expect=main.TRUE, actual=topology,
641 onpass="Link batch up successful",
642 onfail="Link batch up failed" )
643
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800644 @staticmethod
You Wang85747762018-05-11 15:51:50 -0700645 def disablePortBatch( main, ports, switches=None, links=None, sleep=None ):
646 """
647 Disable a list of switch ports using 'portstate' and verify ONOS can see the proper link change
648 ports: a list of ports to disable ex. [ [ "of:0000000000000001", 1 ] ]
649 switches, links: number of expected switches and links after link change, ex.: '4', '6'
650 """
651 if sleep is None:
652 sleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
653 else:
654 sleep = float( sleep )
655 main.step( "Disable a batch of ports" )
656 for dpid, port in ports:
657 main.Cluster.active( 0 ).CLI.portstate( dpid=dpid, port=port, state="disable" )
658 main.log.info( "Waiting {} seconds for port down to be discovered".format( sleep ) )
659 time.sleep( sleep )
660 if switches and links:
661 result = main.Cluster.active( 0 ).CLI.checkStatus( numoswitch=switches,
662 numolink=links )
663 utilities.assert_equals( expect=main.TRUE, actual=result,
664 onpass="Port down successful",
665 onfail="Port down failed" )
666
667 @staticmethod
668 def enablePortBatch( main, ports, switches, links, sleep=None ):
669 """
670 Enable a list of switch ports using 'portstate' and verify ONOS can see the proper link change
671 ports: a list of ports to enable ex. [ [ "of:0000000000000001", 1 ] ]
672 switches, links: number of expected switches and links after link change, ex.: '4', '6'
673 """
674 if sleep is None:
675 sleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
676 else:
677 sleep = float( sleep )
678 main.step( "Enable a batch of ports" )
679 for dpid, port in ports:
680 main.Cluster.active( 0 ).CLI.portstate( dpid=dpid, port=port, state="enable" )
681 main.log.info( "Waiting {} seconds for port up to be discovered".format( sleep ) )
682 time.sleep( sleep )
683 if switches and links:
684 result = main.Cluster.active( 0 ).CLI.checkStatus( numoswitch=switches,
685 numolink=links )
686 utilities.assert_equals( expect=main.TRUE, actual=result,
687 onpass="Port up successful",
688 onfail="Port up failed" )
689
690 @staticmethod
You Wangc02d8352018-04-17 16:42:10 -0700691 def restoreLink( main, end1, end2, switches, links,
Jon Halla604fd42018-05-04 14:27:27 -0700692 portUp=False, dpid1='', dpid2='', port1='', port2='', sleep=None ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700693 """
694 Params:
695 end1,end2: identify the end switches, ex.: 'leaf1', 'spine1'
You Wangc02d8352018-04-17 16:42:10 -0700696 portUp: enable portstate after restoring link
Jon Hall1efcb3f2016-08-23 13:42:15 -0700697 dpid1, dpid2: dpid of the end switches respectively, ex.: 'of:0000000000000002'
698 port1, port2: respective port of the end switches that connects to the link, ex.:'1'
699 switches, links: number of expected switches and links after linkDown, ex.: '4', '6'
700 Kill a link and verify ONOS can see the proper link change
701 """
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700702 main.step( "Restore link between %s and %s" % ( end1, end2 ) )
Jon Halla604fd42018-05-04 14:27:27 -0700703 if sleep is None:
704 sleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
705 else:
706 sleep = float( sleep )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700707 result = False
708 count = 0
709 while True:
710 count += 1
Jon Halla604fd42018-05-04 14:27:27 -0700711 ctrl = main.Cluster.next()
You Wangd5873482018-01-24 12:30:00 -0800712 main.Network.link( END1=end1, END2=end2, OPTION="up" )
713 main.Network.link( END2=end1, END1=end2, OPTION="up" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700714 main.log.info(
Jon Halla604fd42018-05-04 14:27:27 -0700715 "Waiting %s seconds for link up to be discovered" % sleep )
716 time.sleep( sleep )
Pierfb719b12016-09-19 14:51:44 -0700717
You Wangc02d8352018-04-17 16:42:10 -0700718 if portUp:
719 ctrl.CLI.portstate( dpid=dpid1, port=port1, state='Enable' )
720 ctrl.CLI.portstate( dpid=dpid2, port=port2, state='Enable' )
Jon Halla604fd42018-05-04 14:27:27 -0700721 time.sleep( sleep )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700722
Jon Halla604fd42018-05-04 14:27:27 -0700723 result = ctrl.CLI.checkStatus( numoswitch=switches,
724 numolink=links )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700725 if count > 5 or result:
726 break
727 utilities.assert_equals( expect=main.TRUE, actual=result,
728 onpass="Link up successful",
729 onfail="Failed to bring link up" )
730
731 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700732 def killSwitch( main, switch, switches, links, sleep=None ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700733 """
734 Params: switches, links: number of expected switches and links after SwitchDown, ex.: '4', '6'
735 Completely kill a switch and verify ONOS can see the proper change
736 """
Jon Halla604fd42018-05-04 14:27:27 -0700737 if sleep is None:
738 sleep = float( main.params[ 'timers' ][ 'SwitchDiscovery' ] )
739 else:
740 sleep = float( sleep )
You Wangc02d8352018-04-17 16:42:10 -0700741 switch = switch if isinstance( switch, list ) else [ switch ]
742 main.step( "Kill " + str( switch ) )
743 for s in switch:
744 main.log.info( "Stopping " + s )
745 main.Network.switch( SW=s, OPTION="stop" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700746 # todo make this repeatable
Jon Halla604fd42018-05-04 14:27:27 -0700747
748 # TODO: Can remove this, since in the retry we will wait anyways if topology is incorrect
Jon Hall1efcb3f2016-08-23 13:42:15 -0700749 main.log.info( "Waiting %s seconds for switch down to be discovered" % (
Jon Halla604fd42018-05-04 14:27:27 -0700750 sleep ) )
751 time.sleep( sleep )
Devin Lim142b5342017-07-20 15:22:39 -0700752 topology = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
Jon Hall1efcb3f2016-08-23 13:42:15 -0700753 main.FALSE,
754 kwargs={ 'numoswitch': switches,
755 'numolink': links },
756 attempts=10,
Jon Halla604fd42018-05-04 14:27:27 -0700757 sleep=sleep )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700758 utilities.assert_equals( expect=main.TRUE, actual=topology,
759 onpass="Kill switch successful",
760 onfail="Failed to kill switch?" )
761
762 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700763 def recoverSwitch( main, switch, switches, links, rediscoverHosts=False, hostsToDiscover=[], sleep=None ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700764 """
765 Params: switches, links: number of expected switches and links after SwitchUp, ex.: '4', '6'
766 Recover a switch and verify ONOS can see the proper change
767 """
Jon Halla604fd42018-05-04 14:27:27 -0700768 if sleep is None:
769 sleep = float( main.params[ 'timers' ][ 'SwitchDiscovery' ] )
770 else:
771 sleep = float( sleep )
772 # TODO make this repeatable
You Wangc02d8352018-04-17 16:42:10 -0700773 switch = switch if isinstance( switch, list ) else [ switch ]
774 main.step( "Recovering " + str( switch ) )
775 for s in switch:
776 main.log.info( "Starting " + s )
777 main.Network.switch( SW=s, OPTION="start" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700778 main.log.info( "Waiting %s seconds for switch up to be discovered" % (
Jon Halla604fd42018-05-04 14:27:27 -0700779 sleep ) )
780 time.sleep( sleep )
Andreas Pantelopoulos74c7ff22018-05-01 15:42:02 -0700781 if rediscoverHosts:
You Wang48381752018-05-07 13:50:57 -0700782 main.Network.discoverHosts( hostList=hostsToDiscover )
Andreas Pantelopoulos74c7ff22018-05-01 15:42:02 -0700783 main.log.info( "Waiting %s seconds for hosts to get re-discovered" % (
Jon Halla604fd42018-05-04 14:27:27 -0700784 sleep ) )
785 time.sleep( sleep )
Andreas Pantelopoulos74c7ff22018-05-01 15:42:02 -0700786
Devin Lim142b5342017-07-20 15:22:39 -0700787 topology = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
Jon Hall1efcb3f2016-08-23 13:42:15 -0700788 main.FALSE,
789 kwargs={ 'numoswitch': switches,
790 'numolink': links },
791 attempts=10,
Jon Halla604fd42018-05-04 14:27:27 -0700792 sleep=sleep )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700793 utilities.assert_equals( expect=main.TRUE, actual=topology,
794 onpass="Switch recovery successful",
795 onfail="Failed to recover switch?" )
796
Jonghwan Hyun25c98a62018-05-04 13:59:09 -0700797 @staticmethod
You Wang0f745de2018-07-27 15:49:22 -0700798 def killRouter( main, router, sleep=None ):
799 """
800 Kill bgpd process on a quagga router
801 router: name of the router to be killed. E.g. "bgp1"
802 """
803 sleep = float( sleep )
804 main.step( "Kill " + str( router ) )
805 if hasattr( main, 'Mininet1' ):
806 main.Mininet1.handle.sendline( "px {}.stopProtocols()".format( router ) )
807 main.Mininet1.handle.expect( "mininet>" )
808 else:
809 # TODO: support killing router in physical network
810 pass
811 main.log.info( "Waiting %s seconds for router down to be discovered" % ( sleep ) )
812 time.sleep( sleep )
813
814 @staticmethod
815 def recoverRouter( main, router, sleep=None ):
816 """
817 Restart bgpd process on a quagga router
818 router: name of the router to be recovered. E.g. "bgp1"
819 """
820 sleep = float( sleep )
821 main.step( "Recovering " + str( router ) )
822 if hasattr( main, 'Mininet1' ):
823 main.Mininet1.handle.sendline( "px {}.startProtocols()".format( router ) )
824 main.Mininet1.handle.expect( "mininet>" )
825 else:
826 # TODO: support recovering router in physical network
827 pass
828 main.log.info( "Waiting %s seconds for router up to be discovered" % ( sleep ) )
829 time.sleep( sleep )
830
831 @staticmethod
You Wang5da39c82018-04-26 22:55:08 -0700832 def cleanup( main, copyKarafLog=True, removeHostComponent=False ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700833 """
834 Stop Onos-cluster.
835 Stops Mininet
836 Copies ONOS log
837 """
You Wang4cc61912018-08-28 10:10:58 -0700838 from tests.dependencies.utils import Utils
839 main.utils = Utils()
840 # Clean up scapy hosts
You Wange24d6272018-03-27 21:18:50 -0700841 if hasattr( main, "scapyHosts" ):
842 scapyResult = main.TRUE
843 for host in main.scapyHosts:
844 scapyResult = host.stopScapy() and scapyResult
845 main.log.info( "Stopped Scapy Host: {0}".format( host.name ) )
846 for host in main.scapyHosts:
You Wang4cc61912018-08-28 10:10:58 -0700847 if hasattr( main, 'Mininet1' ):
848 scapyResult = main.Scapy.removeHostComponent( host.name ) and scapyResult
849 else:
850 scapyResult = main.Network.removeHostComponent( host.name ) and scapyResult
You Wange24d6272018-03-27 21:18:50 -0700851 main.log.info( "Removed Scapy Host Component: {0}".format( host.name ) )
852 main.scapyHosts = []
853
You Wang5da39c82018-04-26 22:55:08 -0700854 if removeHostComponent:
855 for host in main.internalIpv4Hosts + main.internalIpv6Hosts + main.externalIpv4Hosts + main.externalIpv6Hosts:
856 if hasattr( main, host ):
You Wang4cc61912018-08-28 10:10:58 -0700857 if hasattr( main, 'Mininet1' ):
858 pass
859 else:
860 getattr( main, host ).disconnectInband()
You Wang5da39c82018-04-26 22:55:08 -0700861 main.Network.removeHostComponent( host )
862
You Wang5df1c6d2018-04-06 18:02:02 -0700863 if hasattr( main, 'Mininet1' ):
Pier6a0c4de2018-03-18 16:01:30 -0700864 main.utils.mininetCleanup( main.Mininet1 )
You Wang4cc61912018-08-28 10:10:58 -0700865 else:
866 main.Network.disconnectInbandHosts()
867 main.Network.disconnectFromNet()
Devin Lim58046fa2017-07-05 16:55:00 -0700868
You Wang5df1c6d2018-04-06 18:02:02 -0700869 if copyKarafLog:
870 main.utils.copyKarafLog( "CASE%d" % main.CurrentTestCaseNumber, before=True, includeCaseDesc=False )
Devin Lim58046fa2017-07-05 16:55:00 -0700871
Devin Lim142b5342017-07-20 15:22:39 -0700872 for ctrl in main.Cluster.active():
873 main.ONOSbench.onosStop( ctrl.ipAddress )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700874
875 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700876 def verifyNodes( main ):
877 """
878 Verifies Each active node in the cluster has an accurate view of other node's and their status
879
880 Params:
881 nodes, integer array with position of the ONOS nodes in the CLIs array
882 """
883 nodeResults = utilities.retry( main.Cluster.nodesCheck,
884 False,
885 attempts=10,
886 sleep=10 )
887 utilities.assert_equals( expect=True, actual=nodeResults,
888 onpass="Nodes check successful",
889 onfail="Nodes check NOT successful" )
890
891 if not nodeResults:
892 for ctrl in main.Cluster.runningNodes:
893 main.log.debug( "{} components not ACTIVE: \n{}".format(
894 ctrl.name,
Jon Hall6c9e2da2018-11-06 12:01:23 -0800895 ctrl.CLI.sendline( "onos:scr-list | grep -v ACTIVE" ) ) )
You Wang0bed5932018-12-11 14:45:41 -0800896 main.log.error( "Failed to verify nodes, stopping test" )
Jon Halla604fd42018-05-04 14:27:27 -0700897 main.cleanAndExit()
898
899 @staticmethod
900 def verifyTopology( main, switches, links, expNodes ):
901 """
902 Verifies that the ONOS cluster has an acuurate view of the topology
903
904 Params:
905 switches, links, expNodes: number of expected switches, links, and nodes at this point in the test ex.: '4', '6', '2'
906 """
907 main.step( "Check number of topology elements" )
908 topology = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
909 main.FALSE,
910 kwargs={ 'numoswitch': switches,
911 'numolink': links,
912 'numoctrl': expNodes },
913 attempts=10,
914 sleep=12 )
915 utilities.assert_equals( expect=main.TRUE, actual=topology,
916 onpass="Number of topology elements are correct",
917 onfail="Unexpected number of links, switches, and/or controllers" )
918
919 @staticmethod
920 def killOnos( main, nodes, switches, links, expNodes, sleep=None ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700921 """
922 Params: nodes, integer array with position of the ONOS nodes in the CLIs array
923 switches, links, nodes: number of expected switches, links and nodes after KillOnos, ex.: '4', '6'
924 Completely Kill an ONOS instance and verify the ONOS cluster can see the proper change
925 """
Jon Halla604fd42018-05-04 14:27:27 -0700926 # TODO: We have enough information in the Cluster instance to remove expNodes from here and verifyTopology
Jon Hall3c910162018-03-07 14:42:16 -0800927 main.step( "Killing ONOS instances with index(es): {}".format( nodes ) )
Jon Halla604fd42018-05-04 14:27:27 -0700928 if sleep is None:
929 sleep = float( main.params[ 'timers' ][ 'OnosDiscovery' ] )
930 else:
931 sleep = float( sleep )
Pier3b58c652016-09-26 12:03:31 -0700932
Jon Hall1efcb3f2016-08-23 13:42:15 -0700933 for i in nodes:
Devin Lim142b5342017-07-20 15:22:39 -0700934 killResult = main.ONOSbench.onosDie( main.Cluster.runningNodes[ i ].ipAddress )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700935 utilities.assert_equals( expect=main.TRUE, actual=killResult,
936 onpass="ONOS instance Killed",
937 onfail="Error killing ONOS instance" )
Devin Lim142b5342017-07-20 15:22:39 -0700938 main.Cluster.runningNodes[ i ].active = False
Jon Halla604fd42018-05-04 14:27:27 -0700939 main.Cluster.reset()
940 time.sleep( sleep )
Pier3b58c652016-09-26 12:03:31 -0700941
Devin Lim142b5342017-07-20 15:22:39 -0700942 if len( nodes ) < main.Cluster.numCtrls:
Jon Halla604fd42018-05-04 14:27:27 -0700943 Testcaselib.verifyNodes( main )
944 Testcaselib.verifyTopology( main, switches, links, expNodes )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700945
946 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700947 def recoverOnos( main, nodes, switches, links, expNodes, sleep=None ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700948 """
949 Params: nodes, integer array with position of the ONOS nodes in the CLIs array
950 switches, links, nodes: number of expected switches, links and nodes after recoverOnos, ex.: '4', '6'
951 Recover an ONOS instance and verify the ONOS cluster can see the proper change
952 """
Jon Hall3c910162018-03-07 14:42:16 -0800953 main.step( "Recovering ONOS instances with index(es): {}".format( nodes ) )
Jon Halla604fd42018-05-04 14:27:27 -0700954 if sleep is None:
955 sleep = float( main.params[ 'timers' ][ 'OnosDiscovery' ] )
956 else:
957 sleep = float( sleep )
Devin Lim142b5342017-07-20 15:22:39 -0700958 [ main.ONOSbench.onosStart( main.Cluster.runningNodes[ i ].ipAddress ) for i in nodes ]
Jon Halla604fd42018-05-04 14:27:27 -0700959 time.sleep( sleep )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700960 for i in nodes:
Devin Lim142b5342017-07-20 15:22:39 -0700961 isUp = main.ONOSbench.isup( main.Cluster.runningNodes[ i ].ipAddress )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700962 utilities.assert_equals( expect=main.TRUE, actual=isUp,
963 onpass="ONOS service is ready",
964 onfail="ONOS service did not start properly" )
965 for i in nodes:
966 main.step( "Checking if ONOS CLI is ready" )
Devin Lim142b5342017-07-20 15:22:39 -0700967 ctrl = main.Cluster.runningNodes[ i ]
Jonghwan Hyun76a02b72018-01-30 16:40:48 +0900968 # ctrl.CLI.startCellCli()
Devin Lim142b5342017-07-20 15:22:39 -0700969 cliResult = ctrl.CLI.startOnosCli( ctrl.ipAddress,
970 commandlineTimeout=60,
971 onosStartTimeout=100 )
972 ctrl.active = True
Jon Hall1efcb3f2016-08-23 13:42:15 -0700973 utilities.assert_equals( expect=main.TRUE,
974 actual=cliResult,
975 onpass="ONOS CLI is ready",
976 onfail="ONOS CLI is not ready" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700977
Jon Halla604fd42018-05-04 14:27:27 -0700978 main.Cluster.reset()
Pier3b58c652016-09-26 12:03:31 -0700979 main.step( "Checking ONOS nodes" )
Jon Halla604fd42018-05-04 14:27:27 -0700980 Testcaselib.verifyNodes( main )
981 Testcaselib.verifyTopology( main, switches, links, expNodes )
Pier3b58c652016-09-26 12:03:31 -0700982
Devin Lim142b5342017-07-20 15:22:39 -0700983 ready = utilities.retry( main.Cluster.active( 0 ).CLI.summary,
984 main.FALSE,
985 attempts=10,
986 sleep=12 )
987 if ready:
988 ready = main.TRUE
989 utilities.assert_equals( expect=main.TRUE, actual=ready,
Jon Hall1efcb3f2016-08-23 13:42:15 -0700990 onpass="ONOS summary command succeded",
991 onfail="ONOS summary command failed" )
992 if not ready:
993 main.log.error( "ONOS startup failed!" )
Devin Lim44075962017-08-11 10:56:37 -0700994 main.cleanAndExit()
Jon Hall1efcb3f2016-08-23 13:42:15 -0700995
996 @staticmethod
997 def addHostCfg( main ):
998 """
999 Adds Host Configuration to ONOS
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001000 Updates expected state of the network ( pingChart )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001001 """
1002 import json
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001003 hostCfg = {}
Devin Lim57221b02018-02-14 15:45:36 -08001004 with open( main.configPath + main.forJson + "extra.json" ) as template:
Jon Hall1efcb3f2016-08-23 13:42:15 -07001005 hostCfg = json.load( template )
1006 main.pingChart[ 'ip' ][ 'hosts' ] += [ 'in1' ]
1007 main.step( "Pushing new configuration" )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001008 mac, cfg = hostCfg[ 'hosts' ].popitem()
Devin Lim142b5342017-07-20 15:22:39 -07001009 main.Cluster.active( 0 ).REST.setNetCfg( cfg[ 'basic' ],
1010 subjectClass="hosts",
1011 subjectKey=urllib.quote( mac,
1012 safe='' ),
1013 configKey="basic" )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001014 main.pingChart[ 'ip' ][ 'hosts' ] += [ 'out1' ]
1015 main.step( "Pushing new configuration" )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001016 mac, cfg = hostCfg[ 'hosts' ].popitem()
Devin Lim142b5342017-07-20 15:22:39 -07001017 main.Cluster.active( 0 ).REST.setNetCfg( cfg[ 'basic' ],
1018 subjectClass="hosts",
1019 subjectKey=urllib.quote( mac,
1020 safe='' ),
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001021 configKey="basic" )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001022 main.pingChart.update( { 'vlan1': { "expect": "True",
1023 "hosts": [ "olt1", "vsg1" ] } } )
1024 main.pingChart[ 'vlan5' ][ 'expect' ] = 0
1025 main.pingChart[ 'vlan10' ][ 'expect' ] = 0
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001026 ports = "[%s,%s]" % ( 5, 6 )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001027 cfg = '{"of:0000000000000001":[{"vlan":1,"ports":%s,"name":"OLT 1"}]}' % ports
Devin Lim142b5342017-07-20 15:22:39 -07001028 main.Cluster.active( 0 ).REST.setNetCfg( json.loads( cfg ),
1029 subjectClass="apps",
1030 subjectKey="org.onosproject.segmentrouting",
1031 configKey="xconnect" )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001032
1033 @staticmethod
1034 def delHostCfg( main ):
1035 """
1036 Removest Host Configuration from ONOS
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001037 Updates expected state of the network ( pingChart )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001038 """
1039 import json
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001040 hostCfg = {}
Devin Lim57221b02018-02-14 15:45:36 -08001041 with open( main.configPath + main.forJson + "extra.json" ) as template:
Jon Hall1efcb3f2016-08-23 13:42:15 -07001042 hostCfg = json.load( template )
1043 main.step( "Removing host configuration" )
1044 main.pingChart[ 'ip' ][ 'expect' ] = 0
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001045 mac, cfg = hostCfg[ 'hosts' ].popitem()
Devin Lim142b5342017-07-20 15:22:39 -07001046 main.Cluster.active( 0 ).REST.removeNetCfg( subjectClass="hosts",
1047 subjectKey=urllib.quote(
1048 mac,
1049 safe='' ),
1050 configKey="basic" )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001051 main.step( "Removing configuration" )
1052 main.pingChart[ 'ip' ][ 'expect' ] = 0
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001053 mac, cfg = hostCfg[ 'hosts' ].popitem()
Devin Lim142b5342017-07-20 15:22:39 -07001054 main.Cluster.active( 0 ).REST.removeNetCfg( subjectClass="hosts",
1055 subjectKey=urllib.quote(
1056 mac,
1057 safe='' ),
1058 configKey="basic" )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001059 main.step( "Removing vlan configuration" )
1060 main.pingChart[ 'vlan1' ][ 'expect' ] = 0
Devin Lim142b5342017-07-20 15:22:39 -07001061 main.Cluster.active( 0 ).REST.removeNetCfg( subjectClass="apps",
1062 subjectKey="org.onosproject.segmentrouting",
1063 configKey="xconnect" )
You Wang53dba1e2018-02-02 17:45:44 -08001064
1065 @staticmethod
1066 def verifyNetworkHostIp( main, attempts=10, sleep=10 ):
1067 """
1068 Verifies IP address assignment from the hosts
1069 """
1070 main.step( "Verify IP address assignment from hosts" )
1071 ipResult = main.TRUE
You Wangd66de192018-04-30 17:30:12 -07001072 main.Network.update()
You Wang6acb7a42018-05-04 15:12:25 -07001073 # Find out names of disconnected hosts
1074 disconnectedHosts = []
1075 if hasattr( main, "disconnectedIpv4Hosts" ):
1076 for host in main.disconnectedIpv4Hosts:
1077 disconnectedHosts.append( host )
1078 if hasattr( main, "disconnectedIpv6Hosts" ):
1079 for host in main.disconnectedIpv6Hosts:
1080 disconnectedHosts.append( host )
You Wang53dba1e2018-02-02 17:45:44 -08001081 for hostName, ip in main.expectedHosts[ "network" ].items():
You Wang6acb7a42018-05-04 15:12:25 -07001082 # Exclude disconnected hosts
1083 if hostName in disconnectedHosts:
1084 main.log.debug( "Skip verifying IP for {} as it's disconnected".format( hostName ) )
1085 continue
You Wang53dba1e2018-02-02 17:45:44 -08001086 ipResult = ipResult and utilities.retry( main.Network.verifyHostIp,
1087 main.FALSE,
1088 kwargs={ 'hostList': [ hostName ],
You Wangd66de192018-04-30 17:30:12 -07001089 'prefix': ip,
1090 'update': False },
You Wang53dba1e2018-02-02 17:45:44 -08001091 attempts=attempts,
1092 sleep=sleep )
1093 utilities.assert_equals( expect=main.TRUE, actual=ipResult,
1094 onpass="Verify network host IP succeded",
1095 onfail="Verify network host IP failed" )
1096
1097 @staticmethod
You Wangaec6a092018-08-06 15:36:31 -07001098 def verifyOnosHostIp( main, attempts=10, sleep=10, skipOnFail=True ):
You Wang53dba1e2018-02-02 17:45:44 -08001099 """
1100 Verifies host IP address assignment from ONOS
1101 """
1102 main.step( "Verify host IP address assignment in ONOS" )
1103 ipResult = main.TRUE
You Wang6acb7a42018-05-04 15:12:25 -07001104 # Find out IPs of disconnected hosts
1105 disconnectedIps = []
1106 if hasattr( main, "disconnectedIpv4Hosts" ):
1107 for host in main.disconnectedIpv4Hosts:
1108 disconnectedIps.append( main.expectedHosts[ "network" ][ host ] )
1109 if hasattr( main, "disconnectedIpv6Hosts" ):
1110 for host in main.disconnectedIpv6Hosts:
1111 disconnectedIps.append( main.expectedHosts[ "network" ][ host ] )
You Wang53dba1e2018-02-02 17:45:44 -08001112 for hostName, ip in main.expectedHosts[ "onos" ].items():
You Wang6acb7a42018-05-04 15:12:25 -07001113 # Exclude disconnected hosts
1114 if ip in disconnectedIps:
1115 main.log.debug( "Skip verifying IP for {} as it's disconnected".format( ip ) )
1116 continue
You Wang53dba1e2018-02-02 17:45:44 -08001117 ipResult = ipResult and utilities.retry( main.Cluster.active( 0 ).verifyHostIp,
1118 main.FALSE,
1119 kwargs={ 'hostList': [ hostName ],
1120 'prefix': ip },
1121 attempts=attempts,
1122 sleep=sleep )
1123 utilities.assert_equals( expect=main.TRUE, actual=ipResult,
1124 onpass="Verify ONOS host IP succeded",
1125 onfail="Verify ONOS host IP failed" )
You Wangaec6a092018-08-06 15:36:31 -07001126 if not ipResult and skipOnFail:
1127 Testcaselib.saveOnosDiagnostics( main )
You Wang89477152018-08-07 14:19:02 -07001128 Testcaselib.cleanup( main, copyKarafLog=False )
You Wangaec6a092018-08-06 15:36:31 -07001129 main.skipCase()
Andreas Pantelopoulos2eae3242018-03-06 13:47:20 -08001130
Jonghwan Hyun812c70f2018-02-16 16:33:16 -08001131 @staticmethod
1132 def updateIntfCfg( main, connectPoint, ips=[], untagged=0, tagged=[], native=0 ):
1133 """
1134 Description:
1135 Updates interface configuration in ONOS, with given IP and vlan parameters
1136 Required:
1137 * connectPoint: connect point to update configuration
1138 Optional:
1139 * ips: list of IP addresses, combined with '/xx' subnet representation,
1140 corresponding to 'ips' field in the configuration
1141 * untagged: vlan ID as an integer, corresponding to 'vlan-untagged' field in the configuration
1142 * tagged: integer list of vlan IDs, corresponding to 'vlan-tagged' field in the configuration
1143 * native: vlan ID as an integer, corresponding to 'vlan-native' field in the configuration
1144 """
1145 cfg = dict()
1146 cfg[ "ports" ] = dict()
1147 cfg[ "ports" ][ connectPoint ] = dict()
1148 cfg[ "ports" ][ connectPoint ][ "interfaces" ] = [ dict() ]
1149 cfg[ "ports" ][ connectPoint ][ "interfaces" ][ 0 ][ "ips" ] = ips
1150 if untagged > 0:
1151 cfg[ "ports" ][ connectPoint ][ "interfaces" ][ 0 ][ "vlan-untagged" ] = untagged
1152 else:
1153 cfg[ "ports" ][ connectPoint ][ "interfaces" ][ 0 ][ "vlan-tagged" ] = tagged
1154 if native > 0:
1155 cfg[ "ports" ][ connectPoint ][ "interfaces" ][ 0 ][ "vlan-native" ] = native
1156
1157 main.Cluster.active( 0 ).REST.setNetCfg( json.loads( json.dumps( cfg ) ) )
You Wange24d6272018-03-27 21:18:50 -07001158
1159 @staticmethod
You Wang2cb70172018-07-25 16:44:13 -07001160 def startScapyHosts( main, scapyNames=[], mininetNames=[] ):
You Wange24d6272018-03-27 21:18:50 -07001161 """
1162 Create host components and start Scapy CLIs
You Wang2cb70172018-07-25 16:44:13 -07001163 scapyNames: list of names that will be used as component names for scapy hosts
1164 mininetNames: used when scapy host names are different from the host names
1165 in Mininet. E.g. when scapyNames=['h1Scapy'], it's required to specify the
1166 name of the corresponding Mininet host by mininetNames=['h1']
You Wange24d6272018-03-27 21:18:50 -07001167 """
1168 main.step( "Start Scapy CLIs" )
You Wang4cc61912018-08-28 10:10:58 -07001169 main.scapyNames = scapyNames if scapyNames else main.params[ 'SCAPY' ][ 'HOSTNAMES' ].split( ',' )
1170 main.scapyHosts = [] if not hasattr( main, "scapyHosts" ) else main.scapyHosts
You Wang2cb70172018-07-25 16:44:13 -07001171 for scapyName in main.scapyNames:
You Wang4cc61912018-08-28 10:10:58 -07001172 if hasattr( main, 'Mininet1' ):
1173 main.Scapy.createHostComponent( scapyName )
1174 scapyHandle = getattr( main, scapyName )
1175 if mininetNames:
1176 mininetName = mininetNames[ scapyNames.index( scapyName ) ]
1177 else:
1178 mininetName = None
1179 scapyHandle.startHostCli( mininetName )
You Wang2cb70172018-07-25 16:44:13 -07001180 else:
You Wang0fc21702018-11-02 17:49:18 -07001181 main.Network.createHostComponent( scapyName )
You Wang4cc61912018-08-28 10:10:58 -07001182 scapyHandle = getattr( main, scapyName )
1183 scapyHandle.connectInband()
1184 main.scapyHosts.append( scapyHandle )
You Wang2cb70172018-07-25 16:44:13 -07001185 scapyHandle.startScapy()
1186 scapyHandle.updateSelf()
1187 main.log.debug( scapyHandle.name )
1188 main.log.debug( scapyHandle.hostIp )
1189 main.log.debug( scapyHandle.hostMac )
1190
1191 @staticmethod
1192 def verifyTraffic( main, srcHosts, dstIp, dstHost, dstIntf, ipv6=False, expect=True, skipOnFail=True, maxRetry=2 ):
1193 """
1194 Verify unicast traffic by pinging from source hosts to the destination IP
1195 and capturing the packets at the destination host using Scapy.
1196 srcHosts: List of host names to send the ping packets
1197 dstIp: destination IP of the ping packets
1198 dstHost: host that runs Scapy to capture the packets
1199 dstIntf: name of the interface on the destination host
1200 expect: use True if the ping is expected to be captured at destination;
1201 Otherwise False
1202 skipOnFail: skip the rest of this test case if result is not expected
1203 maxRetry: number of retries allowed
1204 """
1205 from tests.dependencies.topology import Topology
1206 try:
1207 main.topo
1208 except ( NameError, AttributeError ):
1209 main.topo = Topology()
1210 main.step( "Verify traffic to {} by capturing packets on {}".format( dstIp, dstHost ) )
1211 result = main.TRUE
1212 for srcHost in srcHosts:
1213 trafficResult = main.topo.pingAndCapture( srcHost, dstIp, dstHost, dstIntf, ipv6,
1214 expect, maxRetry, True )
1215 if not trafficResult:
You Wang2cb70172018-07-25 16:44:13 -07001216 result = main.FALSE
1217 main.log.warn( "Scapy result from {} to {} is not as expected".format( srcHost, dstIp ) )
1218 utilities.assert_equals( expect=main.TRUE,
1219 actual=result,
1220 onpass="Verify traffic to {}: Pass".format( dstIp ),
1221 onfail="Verify traffic to {}: Fail".format( dstIp ) )
1222 if skipOnFail and result != main.TRUE:
1223 Testcaselib.saveOnosDiagnostics( main )
1224 Testcaselib.cleanup( main, copyKarafLog=False )
1225 main.skipCase()
You Wange24d6272018-03-27 21:18:50 -07001226
1227 @staticmethod
You Wang547893e2018-05-08 13:34:59 -07001228 def verifyMulticastTraffic( main, routeName, expect, skipOnFail=True, maxRetry=1 ):
You Wange24d6272018-03-27 21:18:50 -07001229 """
1230 Verify multicast traffic using scapy
1231 """
You Wangc564c6f2018-05-01 15:24:57 -07001232 from tests.dependencies.topology import Topology
1233 try:
1234 main.topo
1235 except ( NameError, AttributeError ):
1236 main.topo = Topology()
You Wang85747762018-05-11 15:51:50 -07001237 main.step( "Verify {} multicast traffic".format( routeName ) )
You Wangc02d8352018-04-17 16:42:10 -07001238 routeData = main.multicastConfig[ routeName ]
1239 srcs = main.mcastRoutes[ routeName ][ "src" ]
1240 dsts = main.mcastRoutes[ routeName ][ "dst" ]
1241 main.log.info( "Sending multicast traffic from {} to {}".format( [ routeData[ "src" ][ i ][ "host" ] for i in srcs ],
1242 [ routeData[ "dst" ][ i ][ "host" ] for i in dsts ] ) )
You Wang85747762018-05-11 15:51:50 -07001243 result = main.TRUE
You Wangc02d8352018-04-17 16:42:10 -07001244 for src in srcs:
1245 srcEntry = routeData[ "src" ][ src ]
1246 for dst in dsts:
1247 dstEntry = routeData[ "dst" ][ dst ]
1248 sender = getattr( main, srcEntry[ "host" ] )
1249 receiver = getattr( main, dstEntry[ "host" ] )
1250 main.Network.addRoute( str( srcEntry[ "host" ] ),
1251 str( routeData[ "group" ] ),
1252 str( srcEntry[ "interface" ] ),
1253 True if routeData[ "ipVersion" ] == 6 else False )
1254 # Build the packet
1255 sender.buildEther( dst=str( srcEntry[ "Ether" ] ) )
1256 if routeData[ "ipVersion" ] == 4:
1257 sender.buildIP( dst=str( routeData[ "group" ] ) )
1258 elif routeData[ "ipVersion" ] == 6:
1259 sender.buildIPv6( dst=str( routeData[ "group" ] ) )
1260 sender.buildUDP( ipVersion=routeData[ "ipVersion" ], dport=srcEntry[ "UDP" ] )
1261 sIface = srcEntry[ "interface" ]
1262 dIface = dstEntry[ "interface" ] if "interface" in dstEntry.keys() else None
1263 pktFilter = srcEntry[ "filter" ]
1264 pkt = srcEntry[ "packet" ]
1265 # Send packet and check received packet
1266 expectedResult = expect.pop( 0 ) if isinstance( expect, list ) else expect
You Wangc564c6f2018-05-01 15:24:57 -07001267 t3Cmd = "t3-troubleshoot -vv -sp {} -et ipv{} -d {} -dm {}".format( srcEntry[ "port" ], routeData[ "ipVersion" ],
Jon Halla604fd42018-05-04 14:27:27 -07001268 routeData[ "group" ], srcEntry[ "Ether" ] )
You Wangc564c6f2018-05-01 15:24:57 -07001269 trafficResult = main.topo.sendScapyPackets( sender, receiver, pktFilter, pkt, sIface, dIface,
1270 expectedResult, maxRetry, True, t3Cmd )
You Wang85747762018-05-11 15:51:50 -07001271 if not trafficResult:
1272 result = main.FALSE
1273 main.log.warn( "Scapy result from {} to {} is not as expected".format( srcEntry[ "host" ],
1274 dstEntry[ "host" ] ) )
1275 utilities.assert_equals( expect=main.TRUE,
1276 actual=result,
1277 onpass="Verify {} multicast traffic: Pass".format( routeName ),
1278 onfail="Verify {} multicast traffic: Fail".format( routeName ) )
You Wangba823f02018-05-17 13:54:08 -07001279 if skipOnFail and result != main.TRUE:
You Wang85747762018-05-11 15:51:50 -07001280 Testcaselib.saveOnosDiagnostics( main )
1281 Testcaselib.cleanup( main, copyKarafLog=False )
1282 main.skipCase()
You Wangc02d8352018-04-17 16:42:10 -07001283
1284 @staticmethod
You Wang85747762018-05-11 15:51:50 -07001285 def verifyPing( main, srcList, dstList, ipv6=False, expect=True, wait=1,
You Wang54b1d672018-06-11 16:44:13 -07001286 acceptableFailed=0, skipOnFail=True, stepMsg="Verify Ping",
1287 t3Simple=True ):
You Wang5da39c82018-04-26 22:55:08 -07001288 """
1289 Verify reachability from each host in srcList to each host in dstList
1290 """
1291 from tests.dependencies.topology import Topology
1292 try:
1293 main.topo
1294 except ( NameError, AttributeError ):
1295 main.topo = Topology()
You Wang85747762018-05-11 15:51:50 -07001296 main.step( stepMsg )
You Wang54b1d672018-06-11 16:44:13 -07001297 pingResult = main.topo.ping( srcList, dstList, ipv6, expect, wait, acceptableFailed, skipOnFail, t3Simple )
You Wang85747762018-05-11 15:51:50 -07001298 utilities.assert_equals( expect=main.TRUE,
1299 actual=pingResult,
1300 onpass="{}: Pass".format( stepMsg ),
1301 onfail="{}: Fail".format( stepMsg ) )
You Wang5da39c82018-04-26 22:55:08 -07001302 if not pingResult and skipOnFail:
1303 Testcaselib.saveOnosDiagnostics( main )
1304 Testcaselib.cleanup( main, copyKarafLog=False, removeHostComponent=True )
1305 main.skipCase()
You Wangbc898b82018-05-03 16:22:34 -07001306
1307 @staticmethod
You Wang85747762018-05-11 15:51:50 -07001308 def verifyHostLocations( main, locationDict, retry=2 ):
You Wangbc898b82018-05-03 16:22:34 -07001309 """
1310 Verify if the specified host is discovered by ONOS on the given locations
1311 Required:
You Wang85747762018-05-11 15:51:50 -07001312 locationDict: a dictionary that maps host names to expected locations.
1313 locations could be a string or a list.
1314 ex. { "h1v4": ["of:0000000000000005/8"] }
You Wangbc898b82018-05-03 16:22:34 -07001315 Returns:
1316 main.TRUE if host is discovered on all locations provided, otherwise main.FALSE
1317 """
You Wang85747762018-05-11 15:51:50 -07001318 main.step( "Verify locations of hosts {}".format( locationDict.keys() ) )
1319 result = main.TRUE
1320 for hostName, locations in locationDict.items():
1321 main.log.info( "Verify host {} is discovered at {}".format( hostName, locations ) )
1322 hostIp = main.Network.getIPAddress( hostName, proto='IPV4' )
1323 if not hostIp:
1324 hostIp = main.Network.getIPAddress( hostName, proto='IPV6' )
1325 if not hostIp:
1326 main.log.warn( "Failed to find IP address for host {}, skipping location verification".format( hostName ) )
1327 result = main.FALSE
1328 continue
1329 locationResult = utilities.retry( main.Cluster.active( 0 ).CLI.verifyHostLocation,
1330 main.FALSE,
1331 args=( hostIp, locations ),
1332 attempts=retry + 1,
1333 sleep=10 )
1334 if not locationResult:
1335 result = main.FALSE
1336 main.log.warn( "location verification for host {} failed".format( hostName ) )
You Wang547893e2018-05-08 13:34:59 -07001337 utilities.assert_equals( expect=main.TRUE, actual=result,
You Wang85747762018-05-11 15:51:50 -07001338 onpass="Location verification passed",
1339 onfail="Location verification failed" )
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001340
1341 @staticmethod
You Wang6e5b48e2018-07-23 16:17:38 -07001342 def moveHost( main, hostName, srcSw, dstSw, gw, macAddr=None, prefixLen=None, cfg='', ipv6=False, vlan=None ):
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001343 """
1344 Move specified host from srcSw to dstSw.
1345 If srcSw and dstSw are same, the host will be moved from current port to
1346 next available port.
1347 Required:
1348 hostName: name of the host. e.g., "h1"
1349 srcSw: name of the switch that the host is attached to. e.g., "leaf1"
1350 dstSw: name of the switch that the host will be moved to. e.g., "leaf2"
1351 gw: ip address of the gateway of the new location
1352 Optional:
1353 macAddr: if specified, change MAC address of the host to the specified MAC address.
1354 prefixLen: prefix length
1355 cfg: port configuration as JSON string
1356 ipv6: Use True to move IPv6 host
You Wang6e5b48e2018-07-23 16:17:38 -07001357 vlan: vlan number of the host
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001358 """
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001359 if not hasattr( main, 'Mininet1' ):
1360 main.log.warn( "moveHost is supposed to be used only in Mininet." )
1361 return
1362
You Wang6e5b48e2018-07-23 16:17:38 -07001363 main.step( "Moving {} host {} from {} to {}".format( 'tagged' if vlan else 'untagged', hostName, srcSw, dstSw ) )
1364 main.Mininet1.moveHost( hostName, srcSw, dstSw, macAddr, prefixLen, ipv6, vlan=vlan )
1365 if not ipv6:
You Wang6260ed52018-07-18 17:54:25 -07001366 main.Mininet1.changeDefaultGateway( hostName, gw )
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001367 if cfg:
1368 main.Cluster.active( 0 ).REST.setNetCfg( json.loads( cfg ),
1369 subjectClass="ports" )
You Wang6260ed52018-07-18 17:54:25 -07001370 # Wait for the host to get RA for setting up default gateway
1371 time.sleep( 5 )
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001372
1373 main.Mininet1.discoverHosts( [ hostName, ] )
1374
1375 # Update expectedHost when MAC address is changed.
1376 if macAddr is not None:
1377 ipAddr = main.expectedHosts[ "network" ][ hostName ]
1378 if ipAddr is not None:
1379 for hostName, ip in main.expectedHosts[ "onos" ].items():
1380 if ip == ipAddr:
1381 vlan = hostName.split( "/" )[ -1 ]
1382 del main.expectedHosts[ "onos" ][ hostName ]
You Wang7ea90582018-07-19 15:27:58 -07001383 main.expectedHosts[ "onos" ][ "{}/{}".format( macAddr.upper(), vlan ) ] = ip
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001384 break
Jonghwan Hyun5fad1782018-05-21 14:11:16 -07001385
1386 @staticmethod
1387 def moveDualHomedHost( main, hostName, srcSw, srcPairSw, dstSw, dstPairSw, gw,
You Wang6e5b48e2018-07-23 16:17:38 -07001388 macAddr=None, prefixLen=24, cfg='', ipv6=False, vlan=None ):
Jonghwan Hyun5fad1782018-05-21 14:11:16 -07001389 """
1390 Move specified dual-homed host from srcSw-srcPairSw to dstSw-dstPairSw.
1391 If srcSw-srcPairSw and dstSw-dstPairSw are same, the host will be moved from current port
1392 to next available port.
1393 Required:
1394 hostName: name of the host. e.g., "h1"
1395 srcSw: name of the switch that the host is attached to. e.g., "leaf1"
1396 srcPairSw: name of the paired-switch that the host is attached to. e.g., "leaf2"
1397 dstSw: name of the switch that the host will be moved to. e.g., "leaf1"
1398 dstPairSw: name of the paired-switch that the host will be moved to. e.g., "leaf2"
1399 gw: ip address of the gateway of the new location
1400 Optional:
1401 macAddr: if specified, change MAC address of the host to the specified MAC address.
1402 prefixLen: prefix length
1403 cfg: port configurations as JSON string
You Wang7ea90582018-07-19 15:27:58 -07001404 ipv6: Use True to move IPv6 host
You Wang6e5b48e2018-07-23 16:17:38 -07001405 vlan: vlan number of the host
Jonghwan Hyun5fad1782018-05-21 14:11:16 -07001406 """
Jonghwan Hyun5fad1782018-05-21 14:11:16 -07001407 if not hasattr( main, 'Mininet1' ):
1408 main.log.warn( "moveDualHomedHost is supposed to be used only in Mininet." )
1409 return
1410
You Wang6e5b48e2018-07-23 16:17:38 -07001411 main.step( "Moving {} host {} from {} and {} to {} and {}".format( 'tagged' if vlan else 'untagged', hostName,
1412 srcSw, srcPairSw, dstSw, dstPairSw ) )
1413 main.Mininet1.moveDualHomedHost( hostName, srcSw, srcPairSw, dstSw, dstPairSw,
1414 macAddr=macAddr, prefixLen=prefixLen, ipv6=ipv6, vlan=vlan )
1415 if not ipv6:
You Wang7ea90582018-07-19 15:27:58 -07001416 main.Mininet1.changeDefaultGateway( hostName, gw )
Jonghwan Hyun5fad1782018-05-21 14:11:16 -07001417 if cfg:
1418 main.Cluster.active( 0 ).REST.setNetCfg( json.loads( cfg ),
1419 subjectClass="ports" )
You Wang7ea90582018-07-19 15:27:58 -07001420 # Wait for the host to get RA for setting up default gateway
1421 time.sleep( 5 )
Jonghwan Hyun5fad1782018-05-21 14:11:16 -07001422
1423 main.Mininet1.discoverHosts( [ hostName, ] )
1424
1425 # Update expectedHost when MAC address is changed.
1426 if macAddr is not None:
1427 ipAddr = main.expectedHosts[ "network" ][ hostName ]
1428 if ipAddr is not None:
1429 for hostName, ip in main.expectedHosts[ "onos" ].items():
1430 if ip == ipAddr:
1431 vlan = hostName.split( "/" )[ -1 ]
1432 del main.expectedHosts[ "onos" ][ hostName ]
You Wang7ea90582018-07-19 15:27:58 -07001433 main.expectedHosts[ "onos" ][ "{}/{}".format( macAddr.upper(), vlan ) ] = ip