blob: efd886866f7edb2056917035e0e4fd9e821c1ced [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'
You Wang68568b12019-03-04 11:49:57 -080062 if main.params[ 'DEPENDENCY' ].get( 'useBmv2' ):
63 main.useBmv2 = main.params[ 'DEPENDENCY' ][ 'useBmv2' ] == 'True'
64 else:
65 main.useBmv2 = False
Devin Lim57221b02018-02-14 15:45:36 -080066 main.configPath = main.path + ( "/.." if main.useCommonConf else "" ) + "/dependencies/"
Jon Hallbc1c1c92020-05-27 09:29:30 -070067 main.bmv2Path = "/tools/dev/mininet/"
Devin Lim57221b02018-02-14 15:45:36 -080068 main.forJson = "json/"
69 main.forChart = "chart/"
70 main.forConfig = "conf/"
71 main.forHost = "host/"
You Wang27317572018-03-06 12:13:11 -080072 main.forSwitchFailure = "switchFailure/"
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -080073 main.forLinkFailure = "linkFailure/"
You Wange24d6272018-03-27 21:18:50 -070074 main.forMulticast = "multicast/"
Devin Lim58046fa2017-07-05 16:55:00 -070075 main.topology = main.params[ 'DEPENDENCY' ][ 'topology' ]
You Wangd87b2312018-01-30 12:47:17 -080076 main.topologyLib = main.params[ 'DEPENDENCY' ][ 'lib' ] if 'lib' in main.params[ 'DEPENDENCY' ] else None
77 main.topologyConf = main.params[ 'DEPENDENCY' ][ 'conf' ] if 'conf' in main.params[ 'DEPENDENCY' ] else None
You Wang68568b12019-03-04 11:49:57 -080078 main.bmv2 = "bmv2.py"
Jon Halldac3eae2020-06-05 12:04:06 -070079 main.stratumRoot = main.params[ 'DEPENDENCY'][ 'stratumRoot'] if 'stratumRoot' in main.params[ 'DEPENDENCY' ] else None
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -070080 main.scale = ( main.params[ 'SCALE' ][ 'size' ] ).split( "," )
Devin Lim58046fa2017-07-05 16:55:00 -070081 main.maxNodes = int( main.params[ 'SCALE' ][ 'max' ] )
You Wang5bf49592020-07-08 18:47:46 -070082 main.trellisOar = main.params[ 'DEPENDENCY' ][ 'trellisOar' ]
83 main.t3Oar = main.params[ 'DEPENDENCY' ][ 't3Oar' ] if 't3Oar' in main.params[ 'DEPENDENCY' ] else None
Jon Hall1efcb3f2016-08-23 13:42:15 -070084
Devin Lim0c972b72018-02-08 14:53:59 -080085 stepResult = main.testSetUp.envSetup( False )
Devin Lim58046fa2017-07-05 16:55:00 -070086 except Exception as e:
87 main.testSetUp.envSetupException( e )
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -070088
Devin Lim58046fa2017-07-05 16:55:00 -070089 main.testSetUp.evnSetupConclusion( stepResult )
Jon Hall1efcb3f2016-08-23 13:42:15 -070090
Jon Hall1efcb3f2016-08-23 13:42:15 -070091 @staticmethod
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -080092 def installOnos( main, vlanCfg=True, skipPackage=False, cliSleep=10,
93 parallel=True ):
Jon Hall1efcb3f2016-08-23 13:42:15 -070094 """
95 - Set up cell
96 - Create cell file
97 - Set cell file
98 - Verify cell file
99 - Kill ONOS process
100 - Uninstall ONOS cluster
101 - Verify ONOS start up
102 - Install ONOS cluster
103 - Connect to cli
104 """
105 # main.scale[ 0 ] determines the current number of ONOS controller
You Wangd87b2312018-01-30 12:47:17 -0800106 if not main.apps:
Jon Hall1efcb3f2016-08-23 13:42:15 -0700107 main.log.error( "App list is empty" )
Jon Hall3c910162018-03-07 14:42:16 -0800108 main.log.info( "Cluster size: " + str( main.Cluster.numCtrls ) )
109 main.log.info( "Cluster ips: " + ', '.join( main.Cluster.getIps() ) )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700110 main.dynamicHosts = [ 'in1', 'out1' ]
You Wanga0f6ff62018-01-11 15:46:30 -0800111 main.testSetUp.ONOSSetUp( main.Cluster, newCell=True, cellName=main.cellName,
Andreas Pantelopoulos90f0b102018-02-01 13:21:45 -0800112 skipPack=skipPackage,
113 useSSH=Testcaselib.useSSH,
Devin Lim0c972b72018-02-08 14:53:59 -0800114 installParallel=parallel, includeCaseDesc=False )
Devin Lim142b5342017-07-20 15:22:39 -0700115 ready = utilities.retry( main.Cluster.active( 0 ).CLI.summary,
You Wang5bf49592020-07-08 18:47:46 -0700116 [ None, main.FALSE ],
You Wang1cdc5f52017-12-19 16:47:51 -0800117 sleep=cliSleep,
Devin Lim142b5342017-07-20 15:22:39 -0700118 attempts=10 )
119 if ready:
120 ready = main.TRUE
121 utilities.assert_equals( expect=main.TRUE, actual=ready,
Jon Hall1efcb3f2016-08-23 13:42:15 -0700122 onpass="ONOS summary command succeded",
123 onfail="ONOS summary command failed" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700124 if not ready:
125 main.log.error( "ONOS startup failed!" )
Devin Lim44075962017-08-11 10:56:37 -0700126 main.cleanAndExit()
Jon Hall1efcb3f2016-08-23 13:42:15 -0700127
You Wang5bf49592020-07-08 18:47:46 -0700128 # Install segmentrouting and t3 app
129 appInstallResult = main.ONOSbench.onosAppInstall( main.Cluster.runningNodes[0].ipAddress, main.trellisOar)
130 if main.t3Oar:
131 appInstallResult = appInstallResult and main.ONOSbench.onosAppInstall( main.Cluster.runningNodes[0].ipAddress, main.t3Oar)
132 utilities.assert_equals( expect=main.TRUE, actual=appInstallResult,
133 onpass="SR app installation succeded",
134 onfail="SR app installation failed" )
135 if not appInstallResult:
136 main.cleanAndExit()
137
Devin Lim142b5342017-07-20 15:22:39 -0700138 for ctrl in main.Cluster.active():
139 ctrl.CLI.logSet( "DEBUG", "org.onosproject.segmentrouting" )
You Wangf5f104f2018-03-30 17:09:10 -0700140 ctrl.CLI.logSet( "DEBUG", "org.onosproject.driver" )
Devin Lim142b5342017-07-20 15:22:39 -0700141 ctrl.CLI.logSet( "DEBUG", "org.onosproject.net.flowobjective.impl" )
You Wangf5f104f2018-03-30 17:09:10 -0700142 ctrl.CLI.logSet( "DEBUG", "org.onosproject.routeservice.impl" )
143 ctrl.CLI.logSet( "DEBUG", "org.onosproject.routeservice.store" )
144 ctrl.CLI.logSet( "DEBUG", "org.onosproject.routing.fpm" )
Jon Halldac3eae2020-06-05 12:04:06 -0700145 ctrl.CLI.logSet( "DEBUG", "org.onosproject.fpm" )
Jon Hall9677ed32018-04-24 11:16:23 -0700146 ctrl.CLI.logSet( "TRACE", "org.onosproject.events" )
147 ctrl.CLI.logSet( "DEBUG", "org.onosproject.mcast" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700148
Jon Halldac3eae2020-06-05 12:04:06 -0700149 ctrl.CLI.logSet( "TRACE", "org.onosproject.p4runtime" )
150 ctrl.CLI.logSet( "TRACE", "org.onosproject.protocols.p4runtime" )
151 ctrl.CLI.logSet( "TRACE", "org.onosproject.drivers.p4runtime" )
152 ctrl.CLI.logSet( "TRACE", "org.onosproject.protocols.grpc" )
153 ctrl.CLI.logSet( "TRACE", "org.onosproject.protocols.gnmi" )
154 ctrl.CLI.logSet( "TRACE", "org.onosproject.protocols.gnoi" )
155 ctrl.CLI.logSet( "TRACE", "org.onosproject.drivers.gnoi" )
156 ctrl.CLI.logSet( "TRACE", "org.onosproject.drivers.gmni" )
157 ctrl.CLI.logSet( "TRACE", "org.onosproject.drivers.stratum" )
158 ctrl.CLI.logSet( "TRACE", "org.onosproject.bmv2" )
159
Jon Hall1efcb3f2016-08-23 13:42:15 -0700160 @staticmethod
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800161 def loadCount( main ):
162 with open("%s/count/%s.count" % (main.configPath, main.cfgName)) as count:
You Wang5df1c6d2018-04-06 18:02:02 -0700163 main.count = json.load(count)
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800164
165 @staticmethod
Devin Lim57221b02018-02-14 15:45:36 -0800166 def loadJson( main ):
167 with open( "%s%s.json" % ( main.configPath + main.forJson,
168 main.cfgName ) ) as cfg:
169 main.Cluster.active( 0 ).REST.setNetCfg( json.load( cfg ) )
170
171 @staticmethod
172 def loadChart( main ):
173 try:
174 with open( "%s%s.chart" % ( main.configPath + main.forChart,
175 main.cfgName ) ) as chart:
176 main.pingChart = json.load(chart)
177 except IOError:
178 main.log.warn( "No chart file found." )
179
180 @staticmethod
181 def loadHost( main ):
182 with open( "%s%s.host" % ( main.configPath + main.forHost,
183 main.cfgName ) ) as host:
184 main.expectedHosts = json.load( host )
185
186 @staticmethod
You Wang27317572018-03-06 12:13:11 -0800187 def loadSwitchFailureChart( main ):
188 with open( "%s%s.switchFailureChart" % ( main.configPath + main.forSwitchFailure,
189 main.cfgName ) ) as sfc:
190 main.switchFailureChart = json.load( sfc )
191
192 @staticmethod
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800193 def loadLinkFailureChart( main ):
194 with open( "%s%s.linkFailureChart" % ( main.configPath + main.forLinkFailure,
You Wange24d6272018-03-27 21:18:50 -0700195 main.cfgName ) ) as lfc:
196 main.linkFailureChart = json.load( lfc )
197
198 @staticmethod
199 def loadMulticastConfig( main ):
200 with open( "%s%s.multicastConfig" % ( main.configPath + main.forMulticast,
201 main.cfgName ) ) as cfg:
202 main.multicastConfig = json.load( cfg )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800203
204 @staticmethod
Jon Hall1efcb3f2016-08-23 13:42:15 -0700205 def startMininet( main, topology, args="" ):
You Wangd87b2312018-01-30 12:47:17 -0800206 copyResult = main.ONOSbench.scp( main.Mininet1,
207 main.topoPath + main.topology,
You Wang5da39c82018-04-26 22:55:08 -0700208 main.Mininet1.home + "custom",
You Wangd87b2312018-01-30 12:47:17 -0800209 direction="to" )
210 if main.topologyLib:
211 for lib in main.topologyLib.split(","):
212 copyResult = copyResult and main.ONOSbench.scp( main.Mininet1,
213 main.topoPath + lib,
You Wang5da39c82018-04-26 22:55:08 -0700214 main.Mininet1.home + "custom",
You Wangd87b2312018-01-30 12:47:17 -0800215 direction="to" )
216 if main.topologyConf:
You Wanga877ea42018-04-05 15:27:40 -0700217 import re
218 controllerIPs = [ ctrl.ipAddress for ctrl in main.Cluster.runningNodes ]
219 index = 0
You Wangd87b2312018-01-30 12:47:17 -0800220 for conf in main.topologyConf.split(","):
You Wanga877ea42018-04-05 15:27:40 -0700221 # Update zebra configurations with correct ONOS instance IP
222 if conf in [ "zebradbgp1.conf", "zebradbgp2.conf" ]:
223 ip = controllerIPs[ index ]
224 index = ( index + 1 ) % len( controllerIPs )
225 with open( main.configPath + main.forConfig + conf ) as f:
226 s = f.read()
227 s = re.sub( r"(fpm connection ip).*(port 2620)", r"\1 " + ip + r" \2", s )
228 with open( main.configPath + main.forConfig + conf, "w" ) as f:
229 f.write( s )
You Wangd87b2312018-01-30 12:47:17 -0800230 copyResult = copyResult and main.ONOSbench.scp( main.Mininet1,
Devin Lim57221b02018-02-14 15:45:36 -0800231 main.configPath + main.forConfig + conf,
You Wangd87b2312018-01-30 12:47:17 -0800232 "~/",
233 direction="to" )
You Wang68568b12019-03-04 11:49:57 -0800234 copyResult = copyResult and main.ONOSbench.scp( main.Mininet1,
Jon Hallbc1c1c92020-05-27 09:29:30 -0700235 main.ONOSbench.home + main.bmv2Path + main.bmv2,
You Wang68568b12019-03-04 11:49:57 -0800236 main.Mininet1.home + "custom",
237 direction="to" )
You Wangd87b2312018-01-30 12:47:17 -0800238 stepResult = copyResult
239 utilities.assert_equals( expect=main.TRUE,
240 actual=stepResult,
241 onpass="Successfully copied topo files",
242 onfail="Failed to copy topo files" )
Jon Halldac3eae2020-06-05 12:04:06 -0700243 if main.stratumRoot:
244 main.Mininet1.handle.sendline( "export STRATUM_ROOT=" + str( main.stratumRoot ) )
245 main.Mininet1.handle.expect( main.Mininet1.prompt )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700246 main.step( "Starting Mininet Topology" )
Jonghwan Hyun3731d6a2017-10-19 11:59:31 -0700247 arg = "--onos-ip=%s %s" % (",".join([ctrl.ipAddress for ctrl in main.Cluster.runningNodes]), args)
Jon Hall1efcb3f2016-08-23 13:42:15 -0700248 main.topology = topology
Jon Halldac3eae2020-06-05 12:04:06 -0700249 #switchType = " --switch=stratum"
250 #arg += switchType
Jon Hall1efcb3f2016-08-23 13:42:15 -0700251 topoResult = main.Mininet1.startNet(
You Wang5da39c82018-04-26 22:55:08 -0700252 topoFile=main.Mininet1.home + "custom/" + main.topology, args=arg )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700253 stepResult = topoResult
254 utilities.assert_equals( expect=main.TRUE,
255 actual=stepResult,
256 onpass="Successfully loaded topology",
257 onfail="Failed to load topology" )
258 # Exit if topology did not load properly
259 if not topoResult:
Devin Lim44075962017-08-11 10:56:37 -0700260 main.cleanAndExit()
Jon Hall1efcb3f2016-08-23 13:42:15 -0700261
262 @staticmethod
You Wang4cc61912018-08-28 10:10:58 -0700263 def connectToPhysicalNetwork( main ):
You Wang84f981d2018-01-12 16:11:50 -0800264 main.step( "Connecting to physical netowrk" )
265 topoResult = main.NetworkBench.connectToNet()
266 stepResult = topoResult
267 utilities.assert_equals( expect=main.TRUE,
268 actual=stepResult,
269 onpass="Successfully loaded topology",
270 onfail="Failed to load topology" )
271 # Exit if topology did not load properly
272 if not topoResult:
273 main.cleanAndExit()
274
275 main.step( "Assign switches to controllers." )
276 assignResult = main.TRUE
You Wang4cc61912018-08-28 10:10:58 -0700277 switches = main.NetworkBench.getSwitches()
278 pool = []
279 for name in switches.keys():
280 thread = main.Thread( target=main.NetworkBench.assignSwController,
281 name="assignSwitchToController",
282 args=[ name, main.Cluster.getIps(), '6653' ] )
283 pool.append( thread )
284 thread.start()
285 for thread in pool:
286 thread.join( 300 )
287 if not thread.result:
288 stepResult = main.FALSE
You Wang84f981d2018-01-12 16:11:50 -0800289 utilities.assert_equals( expect=main.TRUE,
290 actual=stepResult,
291 onpass="Successfully assign switches to controllers",
292 onfail="Failed to assign switches to controllers" )
293
You Wang4cc61912018-08-28 10:10:58 -0700294 # Check devices
295 Testcaselib.checkDevices( main, switches=int( main.params[ 'TOPO' ][ 'switchNum' ] ) )
296 time.sleep( float( main.params[ "timers" ][ "connectToNetSleep" ] ) )
297 # Connecting to hosts that only have data plane connectivity
298 main.step( "Connecting inband hosts" )
299 stepResult = main.Network.connectInbandHosts()
300 utilities.assert_equals( expect=main.TRUE,
301 actual=stepResult,
302 onpass="Successfully connected inband hosts",
303 onfail="Failed to connect inband hosts" )
304
You Wang84f981d2018-01-12 16:11:50 -0800305 @staticmethod
You Wang5df1c6d2018-04-06 18:02:02 -0700306 def saveOnosDiagnostics( main ):
307 """
308 Get onos-diags.tar.gz and save it to the log directory.
309 suffix: suffix string of the file name. E.g. onos-diags-case1.tar.gz
310 """
311 main.log.info( "Collecting onos-diags..." )
312 main.ONOSbench.onosDiagnostics( [ctrl.ipAddress for ctrl in main.Cluster.runningNodes],
313 main.logdir,
314 "-CASE%d" % main.CurrentTestCaseNumber )
315
316 @staticmethod
Devin Lim142b5342017-07-20 15:22:39 -0700317 def config( main, cfgName ):
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700318 main.spines = []
Piera2a7e1b2016-10-04 11:51:43 -0700319
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700320 main.failures = int( main.params[ 'failures' ] )
321 main.cfgName = cfgName
Piera2a7e1b2016-10-04 11:51:43 -0700322
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700323 if main.cfgName == '2x2':
324 spine = {}
325 spine[ 'name' ] = main.params[ 'switches' ][ 'spine1' ]
326 spine[ 'dpid' ] = main.params[ 'switches' ][ 'spinedpid1' ]
327 main.spines.append( spine )
Piera2a7e1b2016-10-04 11:51:43 -0700328
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700329 spine = {}
330 spine[ 'name' ] = main.params[ 'switches' ][ 'spine2' ]
331 spine[ 'dpid' ] = main.params[ 'switches' ][ 'spinedpid2' ]
332 main.spines.append( spine )
Piera2a7e1b2016-10-04 11:51:43 -0700333
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700334 elif main.cfgName == '4x4':
335 spine = {}
336 spine[ 'name' ] = main.params[ 'switches' ][ 'spine1' ]
337 spine[ 'dpid' ] = main.params[ 'switches' ][ 'spinedpid1' ]
338 main.spines.append( spine )
Piera2a7e1b2016-10-04 11:51:43 -0700339
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700340 spine = {}
341 spine[ 'name' ] = main.params[ 'switches' ][ 'spine2' ]
342 spine[ 'dpid' ] = main.params[ 'switches' ][ 'spinedpid2' ]
343 main.spines.append( spine )
Piera2a7e1b2016-10-04 11:51:43 -0700344
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700345 spine = {}
346 spine[ 'name' ] = main.params[ 'switches' ][ 'spine3' ]
347 spine[ 'dpid' ] = main.params[ 'switches' ][ 'spinedpid3' ]
348 main.spines.append( spine )
Piera2a7e1b2016-10-04 11:51:43 -0700349
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700350 spine = {}
351 spine[ 'name' ] = main.params[ 'switches' ][ 'spine4' ]
352 spine[ 'dpid' ] = main.params[ 'switches' ][ 'spinedpid4' ]
353 main.spines.append( spine )
Piera2a7e1b2016-10-04 11:51:43 -0700354
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700355 else:
Piera2a7e1b2016-10-04 11:51:43 -0700356 main.log.error( "Configuration failed!" )
Devin Lim44075962017-08-11 10:56:37 -0700357 main.cleanAndExit()
You Wang27317572018-03-06 12:13:11 -0800358
Andreas Pantelopoulos2eae3242018-03-06 13:47:20 -0800359 @staticmethod
360 def addStaticOnosRoute( main, subnet, intf):
361 """
362 Adds an ONOS static route with the use route-add command.
363 """
Andreas Pantelopoulos2eae3242018-03-06 13:47:20 -0800364 routeResult = main.Cluster.active( 0 ).addStaticRoute(subnet, intf)
365
Piera2a7e1b2016-10-04 11:51:43 -0700366 @staticmethod
You Wangc02f3be2018-05-18 12:14:23 -0700367 def checkGroupsForBuckets( main, deviceId, subnetDict, routingTable=30 ):
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700368 """
369 Check number of groups for each subnet on device deviceId and matches
370 it with an expected value. subnetDict is a dictionarty containing values
371 of the type "10.0.1.0/24" : 5.
372 """
You Wangc02f3be2018-05-18 12:14:23 -0700373 main.step( "Checking if number of groups for subnets in device {0} is as expected.".format( deviceId ) )
374 groups = main.Cluster.active( 0 ).CLI.getGroups( deviceId, groupType="select" )
375 flows = main.Cluster.active( 0 ).CLI.flows( jsonFormat=False, device=deviceId )
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700376
You Wangc02f3be2018-05-18 12:14:23 -0700377 result = main.TRUE
378 for subnet, numberInSelect in subnetDict.iteritems():
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700379 for flow in flows.splitlines():
You Wangc02f3be2018-05-18 12:14:23 -0700380 if "tableId={0}".format( routingTable ) in flow and subnet in flow:
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700381 # this will match the group id that this flow entry points to, for example :
382 # 0x70000041 in flow entry which contains "deferred=[GROUP:0x70000041], transition=TABLE:60,"
You Wangc02f3be2018-05-18 12:14:23 -0700383 groupId = re.search( r".*GROUP:(0x.*)], transition.*", flow ).groups()[0]
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700384 count = 0
385 for group in groups.splitlines():
You Wangc02f3be2018-05-18 12:14:23 -0700386 if 'id={0}'.format( groupId ) in group:
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700387 count += 1
You Wangc02f3be2018-05-18 12:14:23 -0700388 if count - 1 != numberInSelect:
389 result = main.FALSE
390 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 ) )
391 utilities.assert_equals( expect=main.TRUE, actual=result,
392 onpass="All bucket numbers are as expected",
393 onfail="Some bucket numbers are not as expected" )
Andreas Pantelopoulosdf5061f2018-05-15 11:46:59 -0700394
395 @staticmethod
Jonghwan Hyun76a02b72018-01-30 16:40:48 +0900396 def checkFlows( main, minFlowCount, tag="", dumpflows=True, sleep=10 ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700397 main.step(
Jon Hall3c910162018-03-07 14:42:16 -0800398 "Check whether the flow count is bigger than %s" % minFlowCount )
Jonghwan Hyun76a02b72018-01-30 16:40:48 +0900399 if tag == "":
400 tag = 'CASE%d' % main.CurrentTestCaseNumber
Devin Lim142b5342017-07-20 15:22:39 -0700401 count = utilities.retry( main.Cluster.active( 0 ).CLI.checkFlowCount,
Jon Hall1efcb3f2016-08-23 13:42:15 -0700402 main.FALSE,
403 kwargs={ 'min': minFlowCount },
404 attempts=10,
You Wang1cdc5f52017-12-19 16:47:51 -0800405 sleep=sleep )
steven30801eccfe212019-01-24 13:00:42 +0800406 if count == main.FALSE:
407 count = main.Cluster.active( 0 ).CLI.checkFlowCount()
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700408 utilities.assertEquals(
Jon Hall1efcb3f2016-08-23 13:42:15 -0700409 expect=True,
steven30801eccfe212019-01-24 13:00:42 +0800410 actual=( count > minFlowCount ),
Jon Hall1efcb3f2016-08-23 13:42:15 -0700411 onpass="Flow count looks correct: " + str( count ),
412 onfail="Flow count looks wrong: " + str( count ) )
413
414 main.step( "Check whether all flow status are ADDED" )
Devin Lim142b5342017-07-20 15:22:39 -0700415 flowCheck = utilities.retry( main.Cluster.active( 0 ).CLI.checkFlowsState,
Jon Hall1efcb3f2016-08-23 13:42:15 -0700416 main.FALSE,
417 kwargs={ 'isPENDING': False },
Jonghwan Hyun98fb40a2018-01-04 16:16:28 -0800418 attempts=5,
You Wang1cdc5f52017-12-19 16:47:51 -0800419 sleep=sleep )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700420 utilities.assertEquals(
Jon Hall1efcb3f2016-08-23 13:42:15 -0700421 expect=main.TRUE,
422 actual=flowCheck,
423 onpass="Flow status is correct!",
424 onfail="Flow status is wrong!" )
425 if dumpflows:
Devin Lim142b5342017-07-20 15:22:39 -0700426 main.ONOSbench.dumpONOSCmd( main.Cluster.active( 0 ).ipAddress,
Pier50f0bc62016-09-07 17:53:40 -0700427 "flows",
428 main.logdir,
Jonghwan Hyun76a02b72018-01-30 16:40:48 +0900429 tag + "_FlowsBefore" )
Devin Lim142b5342017-07-20 15:22:39 -0700430 main.ONOSbench.dumpONOSCmd( main.Cluster.active( 0 ).ipAddress,
Pier50f0bc62016-09-07 17:53:40 -0700431 "groups",
432 main.logdir,
Jonghwan Hyun76a02b72018-01-30 16:40:48 +0900433 tag + "_GroupsBefore" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700434
435 @staticmethod
Pier6a0c4de2018-03-18 16:01:30 -0700436 def checkDevices( main, switches, tag="", sleep=10 ):
437 main.step(
438 "Check whether the switches count is equal to %s" % switches )
439 if tag == "":
440 tag = 'CASE%d' % main.CurrentTestCaseNumber
441 result = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
442 main.FALSE,
443 kwargs={ 'numoswitch': switches},
444 attempts=10,
445 sleep=sleep )
446 utilities.assert_equals( expect=main.TRUE, actual=result,
447 onpass="Device up successful",
448 onfail="Failed to boot up devices?" )
449
450 @staticmethod
Jonghwan Hyun98fb40a2018-01-04 16:16:28 -0800451 def checkFlowsByDpid( main, dpid, minFlowCount, sleep=10 ):
452 main.step(
Jonghwan Hyuncf2345c2018-02-26 11:07:54 -0800453 " Check whether the flow count of device %s is bigger than %s" % ( dpid, minFlowCount ) )
454 count = utilities.retry( main.Cluster.active( 0 ).CLI.checkFlowAddedCount,
455 main.FALSE,
456 args=( dpid, minFlowCount ),
Jonghwan Hyun98fb40a2018-01-04 16:16:28 -0800457 attempts=5,
458 sleep=sleep )
steven30801eccfe212019-01-24 13:00:42 +0800459 if count == main.FALSE:
460 count = main.Cluster.active( 0 ).CLI.checkFlowAddedCount( dpid )
Jonghwan Hyun98fb40a2018-01-04 16:16:28 -0800461 utilities.assertEquals(
Jonghwan Hyuncf2345c2018-02-26 11:07:54 -0800462 expect=True,
463 actual=( count > minFlowCount ),
464 onpass="Flow count looks correct: " + str( count ),
steven30801eccfe212019-01-24 13:00:42 +0800465 onfail="Flow count looks wrong: " + str( count ) )
Jonghwan Hyun98fb40a2018-01-04 16:16:28 -0800466
467 @staticmethod
Jon Hall9677ed32018-04-24 11:16:23 -0700468 def checkFlowEqualityByDpid( main, dpid, flowCount, sleep=10 ):
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800469 main.step(
470 " Check whether the flow count of device %s is equal to %s" % ( dpid, flowCount ) )
471 count = utilities.retry( main.Cluster.active( 0 ).CLI.checkFlowAddedCount,
472 main.FALSE,
Jon Hall9677ed32018-04-24 11:16:23 -0700473 args=( dpid, flowCount, False, 1 ),
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800474 attempts=5,
475 sleep=sleep )
steven30801eccfe212019-01-24 13:00:42 +0800476 if count == main.FALSE:
477 count = main.Cluster.active( 0 ).CLI.checkFlowAddedCount( dpid )
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800478 utilities.assertEquals(
479 expect=True,
steven30801eccfe212019-01-24 13:00:42 +0800480 actual=( count == flowCount ),
Jon Hall9677ed32018-04-24 11:16:23 -0700481 onpass="Flow count looks correct: " + str( count ) ,
482 onfail="Flow count looks wrong. found {}, should be {}.".format( count, flowCount ) )
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800483
484 @staticmethod
485 def checkGroupEqualityByDpid( main, dpid, groupCount, sleep=10):
486 main.step(
487 " Check whether the group count of device %s is equal to %s" % ( dpid, groupCount ) )
488 count = utilities.retry( main.Cluster.active( 0 ).CLI.checkGroupAddedCount,
489 main.FALSE,
490 args=( dpid, groupCount, False, 1),
491 attempts=5,
492 sleep=sleep )
steven30801eccfe212019-01-24 13:00:42 +0800493 if count == main.FALSE:
steven3080123997972019-01-29 17:01:40 +0800494 count = main.Cluster.active( 0 ).CLI.checkGroupAddedCount( dpid )
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800495 utilities.assertEquals(
496 expect=True,
497 actual=( count == groupCount ),
Jon Hall9677ed32018-04-24 11:16:23 -0700498 onpass="Group count looks correct: " + str( count ) ,
499 onfail="Group count looks wrong. found {}, should be {}.".format( count, groupCount ) )
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800500
501 @staticmethod
Jon Hall9677ed32018-04-24 11:16:23 -0700502 def checkFlowsGroupsFromFile( main ):
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800503
504 for dpid, values in main.count.items():
505 flowCount = values["flows"]
506 groupCount = values["groups"]
Jon Hall9677ed32018-04-24 11:16:23 -0700507 main.log.report( "Check flow count for dpid " + str( dpid ) +
508 ", should be " + str( flowCount ) )
509 Testcaselib.checkFlowEqualityByDpid( main, dpid, flowCount )
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800510
Jon Hall9677ed32018-04-24 11:16:23 -0700511 main.log.report( "Check group count for dpid " + str( dpid ) +
512 ", should be " + str( groupCount ) )
513 Testcaselib.checkGroupEqualityByDpid( main, dpid, groupCount )
Andreas Pantelopoulos9173d442018-03-01 17:07:37 -0800514
515 return
516
517 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700518 def pingAll( main, tag="", dumpflows=True, acceptableFailed=0, basedOnIp=False,
519 sleep=10, retryAttempts=1, skipOnFail=False ):
You Wangf19d9f42018-02-23 16:34:19 -0800520 '''
You Wangba231e72018-03-01 13:18:21 -0800521 Verify connectivity between hosts according to the ping chart
522 acceptableFailed: max number of acceptable failed pings.
You Wangf19d9f42018-02-23 16:34:19 -0800523 basedOnIp: if True, run ping or ping6 based on suffix of host names
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800524 retryAttempts: the number of retry ping. Only works for IPv4 hosts.
You Wangf19d9f42018-02-23 16:34:19 -0800525 '''
You Wangba231e72018-03-01 13:18:21 -0800526 main.log.report( "Check host connectivity" )
527 main.log.debug( "Ping chart: %s" % main.pingChart )
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800528 if tag == "":
529 tag = 'CASE%d' % main.CurrentTestCaseNumber
530 for entry in main.pingChart.itervalues():
You Wangba231e72018-03-01 13:18:21 -0800531 main.log.debug( "Entry in ping chart: %s" % entry )
532 expect = entry[ 'expect' ]
533 if expect == "Unidirectional":
534 # Verify ping from each src host to each dst host
535 src = entry[ 'src' ]
536 dst = entry[ 'dst' ]
537 expect = main.TRUE
538 main.step( "Verify unidirectional connectivity from %s to %s with tag %s" % ( str( src ), str( dst ), tag ) )
539 if basedOnIp:
540 if ("v4" in src[0]):
541 pa = main.Network.pingallHostsUnidirectional( src, dst, acceptableFailed=acceptableFailed )
542 utilities.assert_equals( expect=expect, actual=pa,
543 onpass="IPv4 connectivity successfully tested",
544 onfail="IPv4 connectivity failed" )
545 if ("v6" in src[0]):
546 pa = main.Network.pingallHostsUnidirectional( src, dst, ipv6=True, acceptableFailed=acceptableFailed )
547 utilities.assert_equals( expect=expect, actual=pa,
548 onpass="IPv6 connectivity successfully tested",
549 onfail="IPv6 connectivity failed" )
550 else:
551 pa = main.Network.pingallHostsUnidirectional( src, dst, acceptableFailed=acceptableFailed )
552 utilities.assert_equals( expect=expect, actual=pa,
553 onpass="IP connectivity successfully tested",
554 onfail="IP connectivity failed" )
555 else:
556 # Verify ping between each host pair
557 hosts = entry[ 'hosts' ]
558 try:
559 expect = main.TRUE if str(expect).lower() == 'true' else main.FALSE
560 except:
561 expect = main.FALSE
562 main.step( "Verify full connectivity for %s with tag %s" % ( str( hosts ), tag ) )
563 if basedOnIp:
564 if ("v4" in hosts[0]):
Jonghwan Hyun812c70f2018-02-16 16:33:16 -0800565 pa = utilities.retry( main.Network.pingallHosts,
566 main.FALSE if expect else main.TRUE,
567 args=(hosts,),
568 attempts=retryAttempts,
569 sleep=sleep )
You Wangba231e72018-03-01 13:18:21 -0800570 utilities.assert_equals( expect=expect, actual=pa,
571 onpass="IPv4 connectivity successfully tested",
572 onfail="IPv4 connectivity failed" )
573 if ("v6" in hosts[0]):
574 pa = main.Network.pingIpv6Hosts( hosts, acceptableFailed=acceptableFailed )
575 utilities.assert_equals( expect=expect, actual=pa,
576 onpass="IPv6 connectivity successfully tested",
577 onfail="IPv6 connectivity failed" )
578 else:
You Wangf19d9f42018-02-23 16:34:19 -0800579 pa = main.Network.pingallHosts( hosts )
580 utilities.assert_equals( expect=expect, actual=pa,
You Wangba231e72018-03-01 13:18:21 -0800581 onpass="IP connectivity successfully tested",
582 onfail="IP connectivity failed" )
You Wang5df1c6d2018-04-06 18:02:02 -0700583 if skipOnFail and pa != expect:
584 Testcaselib.saveOnosDiagnostics( main )
You Wang24ad2f52018-04-10 10:47:12 -0700585 Testcaselib.cleanup( main, copyKarafLog=False )
You Wang5df1c6d2018-04-06 18:02:02 -0700586 main.skipCase()
Andreas Pantelopoulosf6ed5012018-02-08 21:26:01 -0800587
588 if dumpflows:
589 main.ONOSbench.dumpONOSCmd( main.Cluster.active( 0 ).ipAddress,
590 "flows",
591 main.logdir,
592 tag + "_FlowsOn" )
593 main.ONOSbench.dumpONOSCmd( main.Cluster.active( 0 ).ipAddress,
594 "groups",
595 main.logdir,
596 tag + "_GroupsOn" )
597
598 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700599 def killLink( main, end1, end2, switches, links, sleep=None ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700600 """
601 end1,end2: identify the switches, ex.: 'leaf1', 'spine1'
602 switches, links: number of expected switches and links after linkDown, ex.: '4', '6'
603 Kill a link and verify ONOS can see the proper link change
604 """
Jon Halla604fd42018-05-04 14:27:27 -0700605 if sleep is None:
606 sleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
607 else:
608 sleep = float( sleep )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700609 main.step( "Kill link between %s and %s" % ( end1, end2 ) )
Jon Halla604fd42018-05-04 14:27:27 -0700610 linkDown = main.Network.link( END1=end1, END2=end2, OPTION="down" )
611 linkDown = linkDown and main.Network.link( END2=end1, END1=end2, OPTION="down" )
612 # TODO: Can remove this, since in the retry we will wait anyways if topology is incorrect
Jon Hall1efcb3f2016-08-23 13:42:15 -0700613 main.log.info(
Jon Halla604fd42018-05-04 14:27:27 -0700614 "Waiting %s seconds for link down to be discovered" % sleep )
615 time.sleep( sleep )
Devin Lim142b5342017-07-20 15:22:39 -0700616 topology = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
Jon Hall1efcb3f2016-08-23 13:42:15 -0700617 main.FALSE,
618 kwargs={ 'numoswitch': switches,
619 'numolink': links },
620 attempts=10,
Jon Halla604fd42018-05-04 14:27:27 -0700621 sleep=sleep )
622 result = topology and linkDown
Jon Hall1efcb3f2016-08-23 13:42:15 -0700623 utilities.assert_equals( expect=main.TRUE, actual=result,
624 onpass="Link down successful",
625 onfail="Failed to turn off link?" )
626
627 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700628 def killLinkBatch( main, links, linksAfter, switches, sleep=None ):
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800629 """
630 links = list of links (src, dst) to bring down.
631 """
632
633 main.step("Killing a batch of links {0}".format(links))
Jon Halla604fd42018-05-04 14:27:27 -0700634 if sleep is None:
635 sleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
636 else:
637 sleep = float( sleep )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800638
639 for end1, end2 in links:
640 main.Network.link( END1=end1, END2=end2, OPTION="down")
641 main.Network.link( END1=end2, END2=end1, OPTION="down")
642
Jon Halla604fd42018-05-04 14:27:27 -0700643 # TODO: Can remove this, since in the retry we will wait anyways if topology is incorrect
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800644 main.log.info(
Jon Halla604fd42018-05-04 14:27:27 -0700645 "Waiting %s seconds for links down to be discovered" % sleep )
646 time.sleep( sleep )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800647
648 topology = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
649 main.FALSE,
650 kwargs={ 'numoswitch': switches,
651 'numolink': linksAfter },
652 attempts=10,
Jon Halla604fd42018-05-04 14:27:27 -0700653 sleep=sleep )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800654
You Wang2854bce2018-03-30 10:15:32 -0700655 utilities.assert_equals( expect=main.TRUE, actual=topology,
656 onpass="Link batch down successful",
657 onfail="Link batch down failed" )
658
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800659 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700660 def restoreLinkBatch( main, links, linksAfter, switches, sleep=None ):
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800661 """
662 links = list of link (src, dst) to bring up again.
663 """
664
665 main.step("Restoring a batch of links {0}".format(links))
Jon Halla604fd42018-05-04 14:27:27 -0700666 if sleep is None:
667 sleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
668 else:
669 sleep = float( sleep )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800670
671 for end1, end2 in links:
672 main.Network.link( END1=end1, END2=end2, OPTION="up")
673 main.Network.link( END1=end2, END2=end1, OPTION="up")
674
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800675 main.log.info(
Jon Halla604fd42018-05-04 14:27:27 -0700676 "Waiting %s seconds for links up to be discovered" % sleep )
677 time.sleep( sleep )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800678
679 topology = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
680 main.FALSE,
681 kwargs={ 'numoswitch': switches,
682 'numolink': linksAfter },
683 attempts=10,
Jon Halla604fd42018-05-04 14:27:27 -0700684 sleep=sleep )
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800685
You Wang2854bce2018-03-30 10:15:32 -0700686 utilities.assert_equals( expect=main.TRUE, actual=topology,
687 onpass="Link batch up successful",
688 onfail="Link batch up failed" )
689
Andreas Pantelopoulosfab6bf32018-03-06 18:56:35 -0800690 @staticmethod
You Wang85747762018-05-11 15:51:50 -0700691 def disablePortBatch( main, ports, switches=None, links=None, sleep=None ):
692 """
693 Disable a list of switch ports using 'portstate' and verify ONOS can see the proper link change
694 ports: a list of ports to disable ex. [ [ "of:0000000000000001", 1 ] ]
695 switches, links: number of expected switches and links after link change, ex.: '4', '6'
696 """
697 if sleep is None:
698 sleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
699 else:
700 sleep = float( sleep )
701 main.step( "Disable a batch of ports" )
702 for dpid, port in ports:
703 main.Cluster.active( 0 ).CLI.portstate( dpid=dpid, port=port, state="disable" )
704 main.log.info( "Waiting {} seconds for port down to be discovered".format( sleep ) )
705 time.sleep( sleep )
706 if switches and links:
707 result = main.Cluster.active( 0 ).CLI.checkStatus( numoswitch=switches,
708 numolink=links )
709 utilities.assert_equals( expect=main.TRUE, actual=result,
710 onpass="Port down successful",
711 onfail="Port down failed" )
712
713 @staticmethod
714 def enablePortBatch( main, ports, switches, links, sleep=None ):
715 """
716 Enable a list of switch ports using 'portstate' and verify ONOS can see the proper link change
717 ports: a list of ports to enable ex. [ [ "of:0000000000000001", 1 ] ]
718 switches, links: number of expected switches and links after link change, ex.: '4', '6'
719 """
720 if sleep is None:
721 sleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
722 else:
723 sleep = float( sleep )
724 main.step( "Enable a batch of ports" )
725 for dpid, port in ports:
726 main.Cluster.active( 0 ).CLI.portstate( dpid=dpid, port=port, state="enable" )
727 main.log.info( "Waiting {} seconds for port up to be discovered".format( sleep ) )
728 time.sleep( sleep )
729 if switches and links:
730 result = main.Cluster.active( 0 ).CLI.checkStatus( numoswitch=switches,
731 numolink=links )
732 utilities.assert_equals( expect=main.TRUE, actual=result,
733 onpass="Port up successful",
734 onfail="Port up failed" )
735
736 @staticmethod
You Wangc02d8352018-04-17 16:42:10 -0700737 def restoreLink( main, end1, end2, switches, links,
Jon Halla604fd42018-05-04 14:27:27 -0700738 portUp=False, dpid1='', dpid2='', port1='', port2='', sleep=None ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700739 """
740 Params:
741 end1,end2: identify the end switches, ex.: 'leaf1', 'spine1'
You Wangc02d8352018-04-17 16:42:10 -0700742 portUp: enable portstate after restoring link
Jon Hall1efcb3f2016-08-23 13:42:15 -0700743 dpid1, dpid2: dpid of the end switches respectively, ex.: 'of:0000000000000002'
744 port1, port2: respective port of the end switches that connects to the link, ex.:'1'
745 switches, links: number of expected switches and links after linkDown, ex.: '4', '6'
746 Kill a link and verify ONOS can see the proper link change
747 """
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -0700748 main.step( "Restore link between %s and %s" % ( end1, end2 ) )
Jon Halla604fd42018-05-04 14:27:27 -0700749 if sleep is None:
750 sleep = float( main.params[ 'timers' ][ 'LinkDiscovery' ] )
751 else:
752 sleep = float( sleep )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700753 result = False
754 count = 0
755 while True:
756 count += 1
Jon Halla604fd42018-05-04 14:27:27 -0700757 ctrl = main.Cluster.next()
You Wangd5873482018-01-24 12:30:00 -0800758 main.Network.link( END1=end1, END2=end2, OPTION="up" )
759 main.Network.link( END2=end1, END1=end2, OPTION="up" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700760 main.log.info(
Jon Halla604fd42018-05-04 14:27:27 -0700761 "Waiting %s seconds for link up to be discovered" % sleep )
762 time.sleep( sleep )
Pierfb719b12016-09-19 14:51:44 -0700763
You Wangc02d8352018-04-17 16:42:10 -0700764 if portUp:
765 ctrl.CLI.portstate( dpid=dpid1, port=port1, state='Enable' )
766 ctrl.CLI.portstate( dpid=dpid2, port=port2, state='Enable' )
Jon Halla604fd42018-05-04 14:27:27 -0700767 time.sleep( sleep )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700768
Jon Halla604fd42018-05-04 14:27:27 -0700769 result = ctrl.CLI.checkStatus( numoswitch=switches,
770 numolink=links )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700771 if count > 5 or result:
772 break
773 utilities.assert_equals( expect=main.TRUE, actual=result,
774 onpass="Link up successful",
775 onfail="Failed to bring link up" )
776
777 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700778 def killSwitch( main, switch, switches, links, sleep=None ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700779 """
780 Params: switches, links: number of expected switches and links after SwitchDown, ex.: '4', '6'
781 Completely kill a switch and verify ONOS can see the proper change
782 """
Jon Halla604fd42018-05-04 14:27:27 -0700783 if sleep is None:
784 sleep = float( main.params[ 'timers' ][ 'SwitchDiscovery' ] )
785 else:
786 sleep = float( sleep )
You Wangc02d8352018-04-17 16:42:10 -0700787 switch = switch if isinstance( switch, list ) else [ switch ]
788 main.step( "Kill " + str( switch ) )
789 for s in switch:
790 main.log.info( "Stopping " + s )
791 main.Network.switch( SW=s, OPTION="stop" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700792 # todo make this repeatable
Jon Halla604fd42018-05-04 14:27:27 -0700793
794 # TODO: Can remove this, since in the retry we will wait anyways if topology is incorrect
Jon Hall1efcb3f2016-08-23 13:42:15 -0700795 main.log.info( "Waiting %s seconds for switch down to be discovered" % (
Jon Halla604fd42018-05-04 14:27:27 -0700796 sleep ) )
797 time.sleep( sleep )
Devin Lim142b5342017-07-20 15:22:39 -0700798 topology = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
Jon Hall1efcb3f2016-08-23 13:42:15 -0700799 main.FALSE,
800 kwargs={ 'numoswitch': switches,
801 'numolink': links },
802 attempts=10,
Jon Halla604fd42018-05-04 14:27:27 -0700803 sleep=sleep )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700804 utilities.assert_equals( expect=main.TRUE, actual=topology,
805 onpass="Kill switch successful",
806 onfail="Failed to kill switch?" )
807
808 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700809 def recoverSwitch( main, switch, switches, links, rediscoverHosts=False, hostsToDiscover=[], sleep=None ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700810 """
811 Params: switches, links: number of expected switches and links after SwitchUp, ex.: '4', '6'
812 Recover a switch and verify ONOS can see the proper change
813 """
Jon Halla604fd42018-05-04 14:27:27 -0700814 if sleep is None:
815 sleep = float( main.params[ 'timers' ][ 'SwitchDiscovery' ] )
816 else:
817 sleep = float( sleep )
818 # TODO make this repeatable
You Wangc02d8352018-04-17 16:42:10 -0700819 switch = switch if isinstance( switch, list ) else [ switch ]
820 main.step( "Recovering " + str( switch ) )
821 for s in switch:
822 main.log.info( "Starting " + s )
823 main.Network.switch( SW=s, OPTION="start" )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700824 main.log.info( "Waiting %s seconds for switch up to be discovered" % (
Jon Halla604fd42018-05-04 14:27:27 -0700825 sleep ) )
826 time.sleep( sleep )
Andreas Pantelopoulos74c7ff22018-05-01 15:42:02 -0700827 if rediscoverHosts:
You Wang48381752018-05-07 13:50:57 -0700828 main.Network.discoverHosts( hostList=hostsToDiscover )
Andreas Pantelopoulos74c7ff22018-05-01 15:42:02 -0700829 main.log.info( "Waiting %s seconds for hosts to get re-discovered" % (
Jon Halla604fd42018-05-04 14:27:27 -0700830 sleep ) )
831 time.sleep( sleep )
Andreas Pantelopoulos74c7ff22018-05-01 15:42:02 -0700832
Devin Lim142b5342017-07-20 15:22:39 -0700833 topology = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
Jon Hall1efcb3f2016-08-23 13:42:15 -0700834 main.FALSE,
835 kwargs={ 'numoswitch': switches,
836 'numolink': links },
837 attempts=10,
Jon Halla604fd42018-05-04 14:27:27 -0700838 sleep=sleep )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700839 utilities.assert_equals( expect=main.TRUE, actual=topology,
840 onpass="Switch recovery successful",
841 onfail="Failed to recover switch?" )
842
Jonghwan Hyun25c98a62018-05-04 13:59:09 -0700843 @staticmethod
You Wang0f745de2018-07-27 15:49:22 -0700844 def killRouter( main, router, sleep=None ):
845 """
846 Kill bgpd process on a quagga router
847 router: name of the router to be killed. E.g. "bgp1"
848 """
849 sleep = float( sleep )
850 main.step( "Kill " + str( router ) )
851 if hasattr( main, 'Mininet1' ):
852 main.Mininet1.handle.sendline( "px {}.stopProtocols()".format( router ) )
853 main.Mininet1.handle.expect( "mininet>" )
854 else:
855 # TODO: support killing router in physical network
856 pass
857 main.log.info( "Waiting %s seconds for router down to be discovered" % ( sleep ) )
858 time.sleep( sleep )
859
860 @staticmethod
861 def recoverRouter( main, router, sleep=None ):
862 """
863 Restart bgpd process on a quagga router
864 router: name of the router to be recovered. E.g. "bgp1"
865 """
866 sleep = float( sleep )
867 main.step( "Recovering " + str( router ) )
868 if hasattr( main, 'Mininet1' ):
869 main.Mininet1.handle.sendline( "px {}.startProtocols()".format( router ) )
870 main.Mininet1.handle.expect( "mininet>" )
871 else:
872 # TODO: support recovering router in physical network
873 pass
874 main.log.info( "Waiting %s seconds for router up to be discovered" % ( sleep ) )
875 time.sleep( sleep )
876
877 @staticmethod
You Wang5da39c82018-04-26 22:55:08 -0700878 def cleanup( main, copyKarafLog=True, removeHostComponent=False ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700879 """
880 Stop Onos-cluster.
881 Stops Mininet
882 Copies ONOS log
883 """
You Wang4cc61912018-08-28 10:10:58 -0700884 from tests.dependencies.utils import Utils
885 main.utils = Utils()
Jon Halldac3eae2020-06-05 12:04:06 -0700886 for ctrl in main.Cluster.active():
887 ctrl.CLI.log( "\"Ending Test - Shutting down ONOS and Network\"", level="INFO" )
You Wang4cc61912018-08-28 10:10:58 -0700888 # Clean up scapy hosts
You Wange24d6272018-03-27 21:18:50 -0700889 if hasattr( main, "scapyHosts" ):
890 scapyResult = main.TRUE
891 for host in main.scapyHosts:
892 scapyResult = host.stopScapy() and scapyResult
893 main.log.info( "Stopped Scapy Host: {0}".format( host.name ) )
894 for host in main.scapyHosts:
You Wang4cc61912018-08-28 10:10:58 -0700895 if hasattr( main, 'Mininet1' ):
896 scapyResult = main.Scapy.removeHostComponent( host.name ) and scapyResult
897 else:
898 scapyResult = main.Network.removeHostComponent( host.name ) and scapyResult
You Wange24d6272018-03-27 21:18:50 -0700899 main.log.info( "Removed Scapy Host Component: {0}".format( host.name ) )
900 main.scapyHosts = []
901
You Wang5da39c82018-04-26 22:55:08 -0700902 if removeHostComponent:
903 for host in main.internalIpv4Hosts + main.internalIpv6Hosts + main.externalIpv4Hosts + main.externalIpv6Hosts:
904 if hasattr( main, host ):
You Wang4cc61912018-08-28 10:10:58 -0700905 if hasattr( main, 'Mininet1' ):
906 pass
907 else:
908 getattr( main, host ).disconnectInband()
You Wang5da39c82018-04-26 22:55:08 -0700909 main.Network.removeHostComponent( host )
910
You Wang5df1c6d2018-04-06 18:02:02 -0700911 if hasattr( main, 'Mininet1' ):
Pier6a0c4de2018-03-18 16:01:30 -0700912 main.utils.mininetCleanup( main.Mininet1 )
You Wang4cc61912018-08-28 10:10:58 -0700913 else:
914 main.Network.disconnectInbandHosts()
915 main.Network.disconnectFromNet()
Devin Lim58046fa2017-07-05 16:55:00 -0700916
You Wang5df1c6d2018-04-06 18:02:02 -0700917 if copyKarafLog:
918 main.utils.copyKarafLog( "CASE%d" % main.CurrentTestCaseNumber, before=True, includeCaseDesc=False )
Devin Lim58046fa2017-07-05 16:55:00 -0700919
Devin Lim142b5342017-07-20 15:22:39 -0700920 for ctrl in main.Cluster.active():
921 main.ONOSbench.onosStop( ctrl.ipAddress )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700922
923 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700924 def verifyNodes( main ):
925 """
926 Verifies Each active node in the cluster has an accurate view of other node's and their status
927
928 Params:
929 nodes, integer array with position of the ONOS nodes in the CLIs array
930 """
931 nodeResults = utilities.retry( main.Cluster.nodesCheck,
932 False,
933 attempts=10,
934 sleep=10 )
935 utilities.assert_equals( expect=True, actual=nodeResults,
936 onpass="Nodes check successful",
937 onfail="Nodes check NOT successful" )
938
939 if not nodeResults:
940 for ctrl in main.Cluster.runningNodes:
941 main.log.debug( "{} components not ACTIVE: \n{}".format(
942 ctrl.name,
Jon Hall6c9e2da2018-11-06 12:01:23 -0800943 ctrl.CLI.sendline( "onos:scr-list | grep -v ACTIVE" ) ) )
You Wang0bed5932018-12-11 14:45:41 -0800944 main.log.error( "Failed to verify nodes, stopping test" )
Jon Halla604fd42018-05-04 14:27:27 -0700945 main.cleanAndExit()
946
947 @staticmethod
948 def verifyTopology( main, switches, links, expNodes ):
949 """
950 Verifies that the ONOS cluster has an acuurate view of the topology
951
952 Params:
953 switches, links, expNodes: number of expected switches, links, and nodes at this point in the test ex.: '4', '6', '2'
954 """
955 main.step( "Check number of topology elements" )
956 topology = utilities.retry( main.Cluster.active( 0 ).CLI.checkStatus,
957 main.FALSE,
958 kwargs={ 'numoswitch': switches,
959 'numolink': links,
960 'numoctrl': expNodes },
961 attempts=10,
962 sleep=12 )
963 utilities.assert_equals( expect=main.TRUE, actual=topology,
964 onpass="Number of topology elements are correct",
965 onfail="Unexpected number of links, switches, and/or controllers" )
966
967 @staticmethod
968 def killOnos( main, nodes, switches, links, expNodes, sleep=None ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700969 """
970 Params: nodes, integer array with position of the ONOS nodes in the CLIs array
971 switches, links, nodes: number of expected switches, links and nodes after KillOnos, ex.: '4', '6'
972 Completely Kill an ONOS instance and verify the ONOS cluster can see the proper change
973 """
Jon Halla604fd42018-05-04 14:27:27 -0700974 # TODO: We have enough information in the Cluster instance to remove expNodes from here and verifyTopology
Jon Hall3c910162018-03-07 14:42:16 -0800975 main.step( "Killing ONOS instances with index(es): {}".format( nodes ) )
Jon Halla604fd42018-05-04 14:27:27 -0700976 if sleep is None:
977 sleep = float( main.params[ 'timers' ][ 'OnosDiscovery' ] )
978 else:
979 sleep = float( sleep )
Pier3b58c652016-09-26 12:03:31 -0700980
Jon Hall1efcb3f2016-08-23 13:42:15 -0700981 for i in nodes:
Devin Lim142b5342017-07-20 15:22:39 -0700982 killResult = main.ONOSbench.onosDie( main.Cluster.runningNodes[ i ].ipAddress )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700983 utilities.assert_equals( expect=main.TRUE, actual=killResult,
984 onpass="ONOS instance Killed",
985 onfail="Error killing ONOS instance" )
Devin Lim142b5342017-07-20 15:22:39 -0700986 main.Cluster.runningNodes[ i ].active = False
Jon Halla604fd42018-05-04 14:27:27 -0700987 main.Cluster.reset()
988 time.sleep( sleep )
Pier3b58c652016-09-26 12:03:31 -0700989
Devin Lim142b5342017-07-20 15:22:39 -0700990 if len( nodes ) < main.Cluster.numCtrls:
Jon Halla604fd42018-05-04 14:27:27 -0700991 Testcaselib.verifyNodes( main )
992 Testcaselib.verifyTopology( main, switches, links, expNodes )
Jon Hall1efcb3f2016-08-23 13:42:15 -0700993
994 @staticmethod
Jon Halla604fd42018-05-04 14:27:27 -0700995 def recoverOnos( main, nodes, switches, links, expNodes, sleep=None ):
Jon Hall1efcb3f2016-08-23 13:42:15 -0700996 """
997 Params: nodes, integer array with position of the ONOS nodes in the CLIs array
998 switches, links, nodes: number of expected switches, links and nodes after recoverOnos, ex.: '4', '6'
999 Recover an ONOS instance and verify the ONOS cluster can see the proper change
1000 """
Jon Hall3c910162018-03-07 14:42:16 -08001001 main.step( "Recovering ONOS instances with index(es): {}".format( nodes ) )
Jon Halla604fd42018-05-04 14:27:27 -07001002 if sleep is None:
1003 sleep = float( main.params[ 'timers' ][ 'OnosDiscovery' ] )
1004 else:
1005 sleep = float( sleep )
Devin Lim142b5342017-07-20 15:22:39 -07001006 [ main.ONOSbench.onosStart( main.Cluster.runningNodes[ i ].ipAddress ) for i in nodes ]
Jon Halla604fd42018-05-04 14:27:27 -07001007 time.sleep( sleep )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001008 for i in nodes:
Devin Lim142b5342017-07-20 15:22:39 -07001009 isUp = main.ONOSbench.isup( main.Cluster.runningNodes[ i ].ipAddress )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001010 utilities.assert_equals( expect=main.TRUE, actual=isUp,
1011 onpass="ONOS service is ready",
1012 onfail="ONOS service did not start properly" )
1013 for i in nodes:
1014 main.step( "Checking if ONOS CLI is ready" )
Devin Lim142b5342017-07-20 15:22:39 -07001015 ctrl = main.Cluster.runningNodes[ i ]
Jonghwan Hyun76a02b72018-01-30 16:40:48 +09001016 # ctrl.CLI.startCellCli()
Devin Lim142b5342017-07-20 15:22:39 -07001017 cliResult = ctrl.CLI.startOnosCli( ctrl.ipAddress,
1018 commandlineTimeout=60,
1019 onosStartTimeout=100 )
1020 ctrl.active = True
Jon Hall1efcb3f2016-08-23 13:42:15 -07001021 utilities.assert_equals( expect=main.TRUE,
1022 actual=cliResult,
1023 onpass="ONOS CLI is ready",
1024 onfail="ONOS CLI is not ready" )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001025
Jon Halla604fd42018-05-04 14:27:27 -07001026 main.Cluster.reset()
Pier3b58c652016-09-26 12:03:31 -07001027 main.step( "Checking ONOS nodes" )
Jon Halla604fd42018-05-04 14:27:27 -07001028 Testcaselib.verifyNodes( main )
1029 Testcaselib.verifyTopology( main, switches, links, expNodes )
Pier3b58c652016-09-26 12:03:31 -07001030
Devin Lim142b5342017-07-20 15:22:39 -07001031 ready = utilities.retry( main.Cluster.active( 0 ).CLI.summary,
You Wang5bf49592020-07-08 18:47:46 -07001032 [ None, main.FALSE ],
Devin Lim142b5342017-07-20 15:22:39 -07001033 attempts=10,
1034 sleep=12 )
1035 if ready:
1036 ready = main.TRUE
1037 utilities.assert_equals( expect=main.TRUE, actual=ready,
Jon Hall1efcb3f2016-08-23 13:42:15 -07001038 onpass="ONOS summary command succeded",
1039 onfail="ONOS summary command failed" )
1040 if not ready:
1041 main.log.error( "ONOS startup failed!" )
Devin Lim44075962017-08-11 10:56:37 -07001042 main.cleanAndExit()
Jon Hall1efcb3f2016-08-23 13:42:15 -07001043
1044 @staticmethod
1045 def addHostCfg( main ):
1046 """
1047 Adds Host Configuration to ONOS
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001048 Updates expected state of the network ( pingChart )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001049 """
1050 import json
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001051 hostCfg = {}
Devin Lim57221b02018-02-14 15:45:36 -08001052 with open( main.configPath + main.forJson + "extra.json" ) as template:
Jon Hall1efcb3f2016-08-23 13:42:15 -07001053 hostCfg = json.load( template )
1054 main.pingChart[ 'ip' ][ 'hosts' ] += [ 'in1' ]
1055 main.step( "Pushing new configuration" )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001056 mac, cfg = hostCfg[ 'hosts' ].popitem()
Devin Lim142b5342017-07-20 15:22:39 -07001057 main.Cluster.active( 0 ).REST.setNetCfg( cfg[ 'basic' ],
1058 subjectClass="hosts",
1059 subjectKey=urllib.quote( mac,
1060 safe='' ),
1061 configKey="basic" )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001062 main.pingChart[ 'ip' ][ 'hosts' ] += [ 'out1' ]
1063 main.step( "Pushing new configuration" )
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001064 mac, cfg = hostCfg[ 'hosts' ].popitem()
Devin Lim142b5342017-07-20 15:22:39 -07001065 main.Cluster.active( 0 ).REST.setNetCfg( cfg[ 'basic' ],
1066 subjectClass="hosts",
1067 subjectKey=urllib.quote( mac,
1068 safe='' ),
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001069 configKey="basic" )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001070 main.pingChart.update( { 'vlan1': { "expect": "True",
1071 "hosts": [ "olt1", "vsg1" ] } } )
1072 main.pingChart[ 'vlan5' ][ 'expect' ] = 0
1073 main.pingChart[ 'vlan10' ][ 'expect' ] = 0
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001074 ports = "[%s,%s]" % ( 5, 6 )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001075 cfg = '{"of:0000000000000001":[{"vlan":1,"ports":%s,"name":"OLT 1"}]}' % ports
Devin Lim142b5342017-07-20 15:22:39 -07001076 main.Cluster.active( 0 ).REST.setNetCfg( json.loads( cfg ),
1077 subjectClass="apps",
1078 subjectKey="org.onosproject.segmentrouting",
1079 configKey="xconnect" )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001080
1081 @staticmethod
1082 def delHostCfg( main ):
1083 """
1084 Removest Host Configuration from ONOS
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001085 Updates expected state of the network ( pingChart )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001086 """
1087 import json
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001088 hostCfg = {}
Devin Lim57221b02018-02-14 15:45:36 -08001089 with open( main.configPath + main.forJson + "extra.json" ) as template:
Jon Hall1efcb3f2016-08-23 13:42:15 -07001090 hostCfg = json.load( template )
1091 main.step( "Removing host configuration" )
1092 main.pingChart[ 'ip' ][ 'expect' ] = 0
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001093 mac, cfg = hostCfg[ 'hosts' ].popitem()
Devin Lim142b5342017-07-20 15:22:39 -07001094 main.Cluster.active( 0 ).REST.removeNetCfg( subjectClass="hosts",
1095 subjectKey=urllib.quote(
1096 mac,
1097 safe='' ),
1098 configKey="basic" )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001099 main.step( "Removing configuration" )
1100 main.pingChart[ 'ip' ][ 'expect' ] = 0
Jeremy Ronquillo23fb2162017-09-15 14:59:57 -07001101 mac, cfg = hostCfg[ 'hosts' ].popitem()
Devin Lim142b5342017-07-20 15:22:39 -07001102 main.Cluster.active( 0 ).REST.removeNetCfg( subjectClass="hosts",
1103 subjectKey=urllib.quote(
1104 mac,
1105 safe='' ),
1106 configKey="basic" )
Jon Hall1efcb3f2016-08-23 13:42:15 -07001107 main.step( "Removing vlan configuration" )
1108 main.pingChart[ 'vlan1' ][ 'expect' ] = 0
Devin Lim142b5342017-07-20 15:22:39 -07001109 main.Cluster.active( 0 ).REST.removeNetCfg( subjectClass="apps",
1110 subjectKey="org.onosproject.segmentrouting",
1111 configKey="xconnect" )
You Wang53dba1e2018-02-02 17:45:44 -08001112
1113 @staticmethod
1114 def verifyNetworkHostIp( main, attempts=10, sleep=10 ):
1115 """
1116 Verifies IP address assignment from the hosts
1117 """
1118 main.step( "Verify IP address assignment from hosts" )
1119 ipResult = main.TRUE
You Wangd66de192018-04-30 17:30:12 -07001120 main.Network.update()
You Wang6acb7a42018-05-04 15:12:25 -07001121 # Find out names of disconnected hosts
1122 disconnectedHosts = []
1123 if hasattr( main, "disconnectedIpv4Hosts" ):
1124 for host in main.disconnectedIpv4Hosts:
1125 disconnectedHosts.append( host )
1126 if hasattr( main, "disconnectedIpv6Hosts" ):
1127 for host in main.disconnectedIpv6Hosts:
1128 disconnectedHosts.append( host )
You Wang53dba1e2018-02-02 17:45:44 -08001129 for hostName, ip in main.expectedHosts[ "network" ].items():
You Wang6acb7a42018-05-04 15:12:25 -07001130 # Exclude disconnected hosts
1131 if hostName in disconnectedHosts:
1132 main.log.debug( "Skip verifying IP for {} as it's disconnected".format( hostName ) )
1133 continue
You Wang53dba1e2018-02-02 17:45:44 -08001134 ipResult = ipResult and utilities.retry( main.Network.verifyHostIp,
1135 main.FALSE,
1136 kwargs={ 'hostList': [ hostName ],
You Wangd66de192018-04-30 17:30:12 -07001137 'prefix': ip,
1138 'update': False },
You Wang53dba1e2018-02-02 17:45:44 -08001139 attempts=attempts,
1140 sleep=sleep )
1141 utilities.assert_equals( expect=main.TRUE, actual=ipResult,
1142 onpass="Verify network host IP succeded",
1143 onfail="Verify network host IP failed" )
1144
1145 @staticmethod
You Wangaec6a092018-08-06 15:36:31 -07001146 def verifyOnosHostIp( main, attempts=10, sleep=10, skipOnFail=True ):
You Wang53dba1e2018-02-02 17:45:44 -08001147 """
1148 Verifies host IP address assignment from ONOS
1149 """
1150 main.step( "Verify host IP address assignment in ONOS" )
1151 ipResult = main.TRUE
You Wang6acb7a42018-05-04 15:12:25 -07001152 # Find out IPs of disconnected hosts
1153 disconnectedIps = []
1154 if hasattr( main, "disconnectedIpv4Hosts" ):
1155 for host in main.disconnectedIpv4Hosts:
1156 disconnectedIps.append( main.expectedHosts[ "network" ][ host ] )
1157 if hasattr( main, "disconnectedIpv6Hosts" ):
1158 for host in main.disconnectedIpv6Hosts:
1159 disconnectedIps.append( main.expectedHosts[ "network" ][ host ] )
You Wang53dba1e2018-02-02 17:45:44 -08001160 for hostName, ip in main.expectedHosts[ "onos" ].items():
You Wang6acb7a42018-05-04 15:12:25 -07001161 # Exclude disconnected hosts
1162 if ip in disconnectedIps:
1163 main.log.debug( "Skip verifying IP for {} as it's disconnected".format( ip ) )
1164 continue
You Wang53dba1e2018-02-02 17:45:44 -08001165 ipResult = ipResult and utilities.retry( main.Cluster.active( 0 ).verifyHostIp,
1166 main.FALSE,
1167 kwargs={ 'hostList': [ hostName ],
1168 'prefix': ip },
1169 attempts=attempts,
1170 sleep=sleep )
1171 utilities.assert_equals( expect=main.TRUE, actual=ipResult,
1172 onpass="Verify ONOS host IP succeded",
1173 onfail="Verify ONOS host IP failed" )
You Wangaec6a092018-08-06 15:36:31 -07001174 if not ipResult and skipOnFail:
1175 Testcaselib.saveOnosDiagnostics( main )
You Wang89477152018-08-07 14:19:02 -07001176 Testcaselib.cleanup( main, copyKarafLog=False )
You Wangaec6a092018-08-06 15:36:31 -07001177 main.skipCase()
Andreas Pantelopoulos2eae3242018-03-06 13:47:20 -08001178
Jonghwan Hyun812c70f2018-02-16 16:33:16 -08001179 @staticmethod
1180 def updateIntfCfg( main, connectPoint, ips=[], untagged=0, tagged=[], native=0 ):
1181 """
1182 Description:
1183 Updates interface configuration in ONOS, with given IP and vlan parameters
1184 Required:
1185 * connectPoint: connect point to update configuration
1186 Optional:
1187 * ips: list of IP addresses, combined with '/xx' subnet representation,
1188 corresponding to 'ips' field in the configuration
1189 * untagged: vlan ID as an integer, corresponding to 'vlan-untagged' field in the configuration
1190 * tagged: integer list of vlan IDs, corresponding to 'vlan-tagged' field in the configuration
1191 * native: vlan ID as an integer, corresponding to 'vlan-native' field in the configuration
1192 """
1193 cfg = dict()
1194 cfg[ "ports" ] = dict()
1195 cfg[ "ports" ][ connectPoint ] = dict()
1196 cfg[ "ports" ][ connectPoint ][ "interfaces" ] = [ dict() ]
1197 cfg[ "ports" ][ connectPoint ][ "interfaces" ][ 0 ][ "ips" ] = ips
1198 if untagged > 0:
1199 cfg[ "ports" ][ connectPoint ][ "interfaces" ][ 0 ][ "vlan-untagged" ] = untagged
1200 else:
1201 cfg[ "ports" ][ connectPoint ][ "interfaces" ][ 0 ][ "vlan-tagged" ] = tagged
1202 if native > 0:
1203 cfg[ "ports" ][ connectPoint ][ "interfaces" ][ 0 ][ "vlan-native" ] = native
1204
1205 main.Cluster.active( 0 ).REST.setNetCfg( json.loads( json.dumps( cfg ) ) )
You Wange24d6272018-03-27 21:18:50 -07001206
1207 @staticmethod
You Wang2cb70172018-07-25 16:44:13 -07001208 def startScapyHosts( main, scapyNames=[], mininetNames=[] ):
You Wange24d6272018-03-27 21:18:50 -07001209 """
1210 Create host components and start Scapy CLIs
You Wang2cb70172018-07-25 16:44:13 -07001211 scapyNames: list of names that will be used as component names for scapy hosts
1212 mininetNames: used when scapy host names are different from the host names
1213 in Mininet. E.g. when scapyNames=['h1Scapy'], it's required to specify the
1214 name of the corresponding Mininet host by mininetNames=['h1']
You Wange24d6272018-03-27 21:18:50 -07001215 """
1216 main.step( "Start Scapy CLIs" )
You Wang4cc61912018-08-28 10:10:58 -07001217 main.scapyNames = scapyNames if scapyNames else main.params[ 'SCAPY' ][ 'HOSTNAMES' ].split( ',' )
1218 main.scapyHosts = [] if not hasattr( main, "scapyHosts" ) else main.scapyHosts
You Wang2cb70172018-07-25 16:44:13 -07001219 for scapyName in main.scapyNames:
You Wang4cc61912018-08-28 10:10:58 -07001220 if hasattr( main, 'Mininet1' ):
1221 main.Scapy.createHostComponent( scapyName )
1222 scapyHandle = getattr( main, scapyName )
1223 if mininetNames:
1224 mininetName = mininetNames[ scapyNames.index( scapyName ) ]
1225 else:
1226 mininetName = None
1227 scapyHandle.startHostCli( mininetName )
You Wang2cb70172018-07-25 16:44:13 -07001228 else:
You Wang0fc21702018-11-02 17:49:18 -07001229 main.Network.createHostComponent( scapyName )
You Wang4cc61912018-08-28 10:10:58 -07001230 scapyHandle = getattr( main, scapyName )
1231 scapyHandle.connectInband()
1232 main.scapyHosts.append( scapyHandle )
You Wang2cb70172018-07-25 16:44:13 -07001233 scapyHandle.startScapy()
1234 scapyHandle.updateSelf()
1235 main.log.debug( scapyHandle.name )
1236 main.log.debug( scapyHandle.hostIp )
1237 main.log.debug( scapyHandle.hostMac )
1238
1239 @staticmethod
1240 def verifyTraffic( main, srcHosts, dstIp, dstHost, dstIntf, ipv6=False, expect=True, skipOnFail=True, maxRetry=2 ):
1241 """
1242 Verify unicast traffic by pinging from source hosts to the destination IP
1243 and capturing the packets at the destination host using Scapy.
1244 srcHosts: List of host names to send the ping packets
1245 dstIp: destination IP of the ping packets
1246 dstHost: host that runs Scapy to capture the packets
1247 dstIntf: name of the interface on the destination host
1248 expect: use True if the ping is expected to be captured at destination;
1249 Otherwise False
1250 skipOnFail: skip the rest of this test case if result is not expected
1251 maxRetry: number of retries allowed
1252 """
1253 from tests.dependencies.topology import Topology
1254 try:
1255 main.topo
1256 except ( NameError, AttributeError ):
1257 main.topo = Topology()
1258 main.step( "Verify traffic to {} by capturing packets on {}".format( dstIp, dstHost ) )
1259 result = main.TRUE
1260 for srcHost in srcHosts:
1261 trafficResult = main.topo.pingAndCapture( srcHost, dstIp, dstHost, dstIntf, ipv6,
1262 expect, maxRetry, True )
1263 if not trafficResult:
You Wang2cb70172018-07-25 16:44:13 -07001264 result = main.FALSE
1265 main.log.warn( "Scapy result from {} to {} is not as expected".format( srcHost, dstIp ) )
1266 utilities.assert_equals( expect=main.TRUE,
1267 actual=result,
1268 onpass="Verify traffic to {}: Pass".format( dstIp ),
1269 onfail="Verify traffic to {}: Fail".format( dstIp ) )
1270 if skipOnFail and result != main.TRUE:
1271 Testcaselib.saveOnosDiagnostics( main )
1272 Testcaselib.cleanup( main, copyKarafLog=False )
1273 main.skipCase()
You Wange24d6272018-03-27 21:18:50 -07001274
1275 @staticmethod
You Wang547893e2018-05-08 13:34:59 -07001276 def verifyMulticastTraffic( main, routeName, expect, skipOnFail=True, maxRetry=1 ):
You Wange24d6272018-03-27 21:18:50 -07001277 """
1278 Verify multicast traffic using scapy
1279 """
You Wangc564c6f2018-05-01 15:24:57 -07001280 from tests.dependencies.topology import Topology
1281 try:
1282 main.topo
1283 except ( NameError, AttributeError ):
1284 main.topo = Topology()
You Wang85747762018-05-11 15:51:50 -07001285 main.step( "Verify {} multicast traffic".format( routeName ) )
You Wangc02d8352018-04-17 16:42:10 -07001286 routeData = main.multicastConfig[ routeName ]
1287 srcs = main.mcastRoutes[ routeName ][ "src" ]
1288 dsts = main.mcastRoutes[ routeName ][ "dst" ]
1289 main.log.info( "Sending multicast traffic from {} to {}".format( [ routeData[ "src" ][ i ][ "host" ] for i in srcs ],
1290 [ routeData[ "dst" ][ i ][ "host" ] for i in dsts ] ) )
You Wang85747762018-05-11 15:51:50 -07001291 result = main.TRUE
You Wangc02d8352018-04-17 16:42:10 -07001292 for src in srcs:
1293 srcEntry = routeData[ "src" ][ src ]
1294 for dst in dsts:
1295 dstEntry = routeData[ "dst" ][ dst ]
1296 sender = getattr( main, srcEntry[ "host" ] )
1297 receiver = getattr( main, dstEntry[ "host" ] )
1298 main.Network.addRoute( str( srcEntry[ "host" ] ),
1299 str( routeData[ "group" ] ),
1300 str( srcEntry[ "interface" ] ),
1301 True if routeData[ "ipVersion" ] == 6 else False )
1302 # Build the packet
1303 sender.buildEther( dst=str( srcEntry[ "Ether" ] ) )
1304 if routeData[ "ipVersion" ] == 4:
1305 sender.buildIP( dst=str( routeData[ "group" ] ) )
1306 elif routeData[ "ipVersion" ] == 6:
1307 sender.buildIPv6( dst=str( routeData[ "group" ] ) )
1308 sender.buildUDP( ipVersion=routeData[ "ipVersion" ], dport=srcEntry[ "UDP" ] )
1309 sIface = srcEntry[ "interface" ]
1310 dIface = dstEntry[ "interface" ] if "interface" in dstEntry.keys() else None
1311 pktFilter = srcEntry[ "filter" ]
1312 pkt = srcEntry[ "packet" ]
1313 # Send packet and check received packet
1314 expectedResult = expect.pop( 0 ) if isinstance( expect, list ) else expect
You Wangc564c6f2018-05-01 15:24:57 -07001315 t3Cmd = "t3-troubleshoot -vv -sp {} -et ipv{} -d {} -dm {}".format( srcEntry[ "port" ], routeData[ "ipVersion" ],
Jon Halla604fd42018-05-04 14:27:27 -07001316 routeData[ "group" ], srcEntry[ "Ether" ] )
You Wangc564c6f2018-05-01 15:24:57 -07001317 trafficResult = main.topo.sendScapyPackets( sender, receiver, pktFilter, pkt, sIface, dIface,
1318 expectedResult, maxRetry, True, t3Cmd )
You Wang85747762018-05-11 15:51:50 -07001319 if not trafficResult:
1320 result = main.FALSE
1321 main.log.warn( "Scapy result from {} to {} is not as expected".format( srcEntry[ "host" ],
1322 dstEntry[ "host" ] ) )
1323 utilities.assert_equals( expect=main.TRUE,
1324 actual=result,
1325 onpass="Verify {} multicast traffic: Pass".format( routeName ),
1326 onfail="Verify {} multicast traffic: Fail".format( routeName ) )
You Wangba823f02018-05-17 13:54:08 -07001327 if skipOnFail and result != main.TRUE:
You Wang85747762018-05-11 15:51:50 -07001328 Testcaselib.saveOnosDiagnostics( main )
1329 Testcaselib.cleanup( main, copyKarafLog=False )
1330 main.skipCase()
You Wangc02d8352018-04-17 16:42:10 -07001331
1332 @staticmethod
You Wang85747762018-05-11 15:51:50 -07001333 def verifyPing( main, srcList, dstList, ipv6=False, expect=True, wait=1,
You Wang54b1d672018-06-11 16:44:13 -07001334 acceptableFailed=0, skipOnFail=True, stepMsg="Verify Ping",
1335 t3Simple=True ):
You Wang5da39c82018-04-26 22:55:08 -07001336 """
1337 Verify reachability from each host in srcList to each host in dstList
1338 """
1339 from tests.dependencies.topology import Topology
1340 try:
1341 main.topo
1342 except ( NameError, AttributeError ):
1343 main.topo = Topology()
You Wang85747762018-05-11 15:51:50 -07001344 main.step( stepMsg )
You Wang54b1d672018-06-11 16:44:13 -07001345 pingResult = main.topo.ping( srcList, dstList, ipv6, expect, wait, acceptableFailed, skipOnFail, t3Simple )
You Wang85747762018-05-11 15:51:50 -07001346 utilities.assert_equals( expect=main.TRUE,
1347 actual=pingResult,
1348 onpass="{}: Pass".format( stepMsg ),
1349 onfail="{}: Fail".format( stepMsg ) )
You Wang5da39c82018-04-26 22:55:08 -07001350 if not pingResult and skipOnFail:
1351 Testcaselib.saveOnosDiagnostics( main )
1352 Testcaselib.cleanup( main, copyKarafLog=False, removeHostComponent=True )
1353 main.skipCase()
You Wangbc898b82018-05-03 16:22:34 -07001354
1355 @staticmethod
You Wang85747762018-05-11 15:51:50 -07001356 def verifyHostLocations( main, locationDict, retry=2 ):
You Wangbc898b82018-05-03 16:22:34 -07001357 """
1358 Verify if the specified host is discovered by ONOS on the given locations
1359 Required:
You Wang85747762018-05-11 15:51:50 -07001360 locationDict: a dictionary that maps host names to expected locations.
1361 locations could be a string or a list.
1362 ex. { "h1v4": ["of:0000000000000005/8"] }
You Wangbc898b82018-05-03 16:22:34 -07001363 Returns:
1364 main.TRUE if host is discovered on all locations provided, otherwise main.FALSE
1365 """
You Wang85747762018-05-11 15:51:50 -07001366 main.step( "Verify locations of hosts {}".format( locationDict.keys() ) )
1367 result = main.TRUE
1368 for hostName, locations in locationDict.items():
1369 main.log.info( "Verify host {} is discovered at {}".format( hostName, locations ) )
1370 hostIp = main.Network.getIPAddress( hostName, proto='IPV4' )
1371 if not hostIp:
1372 hostIp = main.Network.getIPAddress( hostName, proto='IPV6' )
1373 if not hostIp:
1374 main.log.warn( "Failed to find IP address for host {}, skipping location verification".format( hostName ) )
1375 result = main.FALSE
1376 continue
1377 locationResult = utilities.retry( main.Cluster.active( 0 ).CLI.verifyHostLocation,
1378 main.FALSE,
1379 args=( hostIp, locations ),
1380 attempts=retry + 1,
1381 sleep=10 )
1382 if not locationResult:
1383 result = main.FALSE
1384 main.log.warn( "location verification for host {} failed".format( hostName ) )
You Wang547893e2018-05-08 13:34:59 -07001385 utilities.assert_equals( expect=main.TRUE, actual=result,
You Wang85747762018-05-11 15:51:50 -07001386 onpass="Location verification passed",
1387 onfail="Location verification failed" )
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001388
1389 @staticmethod
You Wang6e5b48e2018-07-23 16:17:38 -07001390 def moveHost( main, hostName, srcSw, dstSw, gw, macAddr=None, prefixLen=None, cfg='', ipv6=False, vlan=None ):
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001391 """
1392 Move specified host from srcSw to dstSw.
1393 If srcSw and dstSw are same, the host will be moved from current port to
1394 next available port.
1395 Required:
1396 hostName: name of the host. e.g., "h1"
1397 srcSw: name of the switch that the host is attached to. e.g., "leaf1"
1398 dstSw: name of the 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 configuration as JSON string
1404 ipv6: Use True to move IPv6 host
You Wang6e5b48e2018-07-23 16:17:38 -07001405 vlan: vlan number of the host
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001406 """
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001407 if not hasattr( main, 'Mininet1' ):
1408 main.log.warn( "moveHost is supposed to be used only in Mininet." )
1409 return
1410
You Wang6e5b48e2018-07-23 16:17:38 -07001411 main.step( "Moving {} host {} from {} to {}".format( 'tagged' if vlan else 'untagged', hostName, srcSw, dstSw ) )
1412 main.Mininet1.moveHost( hostName, srcSw, dstSw, macAddr, prefixLen, ipv6, vlan=vlan )
1413 if not ipv6:
You Wang6260ed52018-07-18 17:54:25 -07001414 main.Mininet1.changeDefaultGateway( hostName, gw )
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001415 if cfg:
1416 main.Cluster.active( 0 ).REST.setNetCfg( json.loads( cfg ),
1417 subjectClass="ports" )
You Wang6260ed52018-07-18 17:54:25 -07001418 # Wait for the host to get RA for setting up default gateway
1419 time.sleep( 5 )
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001420
1421 main.Mininet1.discoverHosts( [ hostName, ] )
1422
1423 # Update expectedHost when MAC address is changed.
1424 if macAddr is not None:
1425 ipAddr = main.expectedHosts[ "network" ][ hostName ]
1426 if ipAddr is not None:
1427 for hostName, ip in main.expectedHosts[ "onos" ].items():
1428 if ip == ipAddr:
1429 vlan = hostName.split( "/" )[ -1 ]
1430 del main.expectedHosts[ "onos" ][ hostName ]
You Wang7ea90582018-07-19 15:27:58 -07001431 main.expectedHosts[ "onos" ][ "{}/{}".format( macAddr.upper(), vlan ) ] = ip
Jonghwan Hyun785471d2018-05-14 14:48:19 -07001432 break
Jonghwan Hyun5fad1782018-05-21 14:11:16 -07001433
1434 @staticmethod
1435 def moveDualHomedHost( main, hostName, srcSw, srcPairSw, dstSw, dstPairSw, gw,
You Wang6e5b48e2018-07-23 16:17:38 -07001436 macAddr=None, prefixLen=24, cfg='', ipv6=False, vlan=None ):
Jonghwan Hyun5fad1782018-05-21 14:11:16 -07001437 """
1438 Move specified dual-homed host from srcSw-srcPairSw to dstSw-dstPairSw.
1439 If srcSw-srcPairSw and dstSw-dstPairSw are same, the host will be moved from current port
1440 to next available port.
1441 Required:
1442 hostName: name of the host. e.g., "h1"
1443 srcSw: name of the switch that the host is attached to. e.g., "leaf1"
1444 srcPairSw: name of the paired-switch that the host is attached to. e.g., "leaf2"
1445 dstSw: name of the switch that the host will be moved to. e.g., "leaf1"
1446 dstPairSw: name of the paired-switch that the host will be moved to. e.g., "leaf2"
1447 gw: ip address of the gateway of the new location
1448 Optional:
1449 macAddr: if specified, change MAC address of the host to the specified MAC address.
1450 prefixLen: prefix length
1451 cfg: port configurations as JSON string
You Wang7ea90582018-07-19 15:27:58 -07001452 ipv6: Use True to move IPv6 host
You Wang6e5b48e2018-07-23 16:17:38 -07001453 vlan: vlan number of the host
Jonghwan Hyun5fad1782018-05-21 14:11:16 -07001454 """
Jonghwan Hyun5fad1782018-05-21 14:11:16 -07001455 if not hasattr( main, 'Mininet1' ):
1456 main.log.warn( "moveDualHomedHost is supposed to be used only in Mininet." )
1457 return
1458
You Wang6e5b48e2018-07-23 16:17:38 -07001459 main.step( "Moving {} host {} from {} and {} to {} and {}".format( 'tagged' if vlan else 'untagged', hostName,
1460 srcSw, srcPairSw, dstSw, dstPairSw ) )
1461 main.Mininet1.moveDualHomedHost( hostName, srcSw, srcPairSw, dstSw, dstPairSw,
1462 macAddr=macAddr, prefixLen=prefixLen, ipv6=ipv6, vlan=vlan )
1463 if not ipv6:
You Wang7ea90582018-07-19 15:27:58 -07001464 main.Mininet1.changeDefaultGateway( hostName, gw )
Jonghwan Hyun5fad1782018-05-21 14:11:16 -07001465 if cfg:
1466 main.Cluster.active( 0 ).REST.setNetCfg( json.loads( cfg ),
1467 subjectClass="ports" )
You Wang7ea90582018-07-19 15:27:58 -07001468 # Wait for the host to get RA for setting up default gateway
1469 time.sleep( 5 )
Jonghwan Hyun5fad1782018-05-21 14:11:16 -07001470
1471 main.Mininet1.discoverHosts( [ hostName, ] )
1472
1473 # Update expectedHost when MAC address is changed.
1474 if macAddr is not None:
1475 ipAddr = main.expectedHosts[ "network" ][ hostName ]
1476 if ipAddr is not None:
1477 for hostName, ip in main.expectedHosts[ "onos" ].items():
1478 if ip == ipAddr:
1479 vlan = hostName.split( "/" )[ -1 ]
1480 del main.expectedHosts[ "onos" ][ hostName ]
You Wang7ea90582018-07-19 15:27:58 -07001481 main.expectedHosts[ "onos" ][ "{}/{}".format( macAddr.upper(), vlan ) ] = ip