blob: 54dbf2931f18fc1792b35f3319647a3625839c86 [file] [log] [blame]
Jeremy Ronquillo3008aa32017-07-07 15:38:57 -07001"""
Jeremy Ronquillob27ce4c2017-07-17 12:41:28 -07002Copyright 2017 Open Networking Foundation (ONF)
3
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
11 (at your option) any later version.
12
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"""
21
22"""
Jeremy Ronquillo3008aa32017-07-07 15:38:57 -070023Testing various connectivity failures for VPLS.
24
25CASE1: Startup.
26CASE2: Load VPLS topology and configurations from demo script.
27CASE50: Initial connectivity test.
28CASE100: Bring down 1 host at a time and test connectivity.
29CASE200: Bring down 1 switch at a time and test connectivity.
30CASE300: Stop 1 ONOS node at a time and test connectivity.
31CASE310: Kill 1 ONOS node at a time and test connectivity.
32CASE400: Bring down 1 link at a time and test connectivity.
33"""
34
35class VPLSfailsafe:
36
37 def __init__( self ):
38 self.default = ''
39
40
41 def CASE1( self, main ):
42 """
43 CASE1 is to compile ONOS and push it to the test machines
44
45 Startup sequence:
46 cell <name>
47 onos-verify-cell
48 NOTE: temporary - onos-remove-raft-logs
49 onos-uninstall
50 start mininet
51 git pull
52 mvn clean install
53 onos-package
54 onos-install -f
55 onos-wait-for-start
56 start cli sessions
57 start tcpdump
58 """
59 import imp
60 import time
61 import json
62
63 main.case( "Setting up test environment" )
64 main.caseExplanation = "Setup the test environment including " +\
65 "installing ONOS, starting Mininet and ONOS" +\
66 "cli sessions."
67
68 # load some variables from the params file
69 cellName = main.params[ 'ENV' ][ 'cellName' ]
70
71 main.numCtrls = int( main.params[ 'num_controllers' ] )
72
73 ofPort = main.params[ 'CTRL' ][ 'port' ]
74
75 main.timeSleep = int( main.params[ 'RETRY' ][ 'sleep' ] )
76 main.numAttempts = int( main.params[ 'RETRY' ][ 'attempts' ] )
77
78 main.CLIs = []
79 main.RESTs = []
80 main.nodes = []
81 ipList = []
82 for i in range( 1, main.numCtrls + 1 ):
83 try:
84 main.CLIs.append( getattr( main, 'ONOScli' + str( i ) ) )
85 main.RESTs.append( getattr( main, 'ONOSrest' + str( i ) ) )
86 main.nodes.append( getattr( main, 'ONOS' + str( i ) ) )
87 ipList.append( main.nodes[ -1 ].ip_address )
88 except AttributeError:
89 break
90
91 main.step( "Create cell file" )
92 cellAppString = main.params[ 'ENV' ][ 'cellApps' ]
93 main.ONOSbench.createCellFile( main.ONOSbench.ip_address, cellName,
94 main.Mininet1.ip_address,
95 cellAppString, ipList, main.ONOScli1.karafUser )
96 main.step( "Applying cell variable to environment" )
97 main.cellResult = main.ONOSbench.setCell( cellName )
98 verifyResult = main.ONOSbench.verifyCell()
99
100 main.log.info( "Uninstalling ONOS" )
101 for node in main.nodes:
102 main.ONOSbench.onosUninstall( node.ip_address )
103
104 # Make sure ONOS is DEAD
105 main.log.info( "Killing any ONOS processes" )
106 killResults = main.TRUE
107 for node in main.nodes:
108 killed = main.ONOSbench.onosKill( node.ip_address )
109 killResults = killResults and killed
110
111 main.step( "Starting Mininet" )
112
113 # scp topo file to mininet
114 # TODO: move to params?
115 topoName = "vpls"
Jeremy Ronquillo31caa482017-07-18 15:55:31 -0700116 topoFile = "vpls.py"
Jeremy Ronquillo3008aa32017-07-07 15:38:57 -0700117 filePath = main.ONOSbench.home + "/tools/test/topos/"
118 main.ONOSbench.scp( main.Mininet1,
119 filePath + topoFile,
120 main.Mininet1.home,
121 direction="to" )
122 topo = " --custom " + main.Mininet1.home + topoFile + " --topo " + topoName
123 args = " --switch ovs,protocols=OpenFlow13"
124 for node in main.nodes:
125 args += " --controller=remote,ip=" + node.ip_address
126 mnCmd = "sudo mn" + topo + args
127 mnResult = main.Mininet1.startNet( mnCmd=mnCmd )
128 utilities.assert_equals( expect=main.TRUE, actual=mnResult,
129 onpass="Mininet Started",
130 onfail="Error starting Mininet" )
131
132 main.ONOSbench.getVersion( report=True )
133
134 main.step( "Creating ONOS package" )
135 packageResult = main.ONOSbench.buckBuild()
136 utilities.assert_equals( expect=main.TRUE, actual=packageResult,
137 onpass="ONOS package successful",
138 onfail="ONOS package failed" )
139
140 main.step( "Installing ONOS package" )
141 onosInstallResult = main.TRUE
142 for node in main.nodes:
143 tmpResult = main.ONOSbench.onosInstall( options="-f",
144 node=node.ip_address )
145 onosInstallResult = onosInstallResult and tmpResult
146 utilities.assert_equals( expect=main.TRUE, actual=onosInstallResult,
147 onpass="ONOS install successful",
148 onfail="ONOS install failed" )
149
150 main.step( "Set up ONOS secure SSH" )
151 secureSshResult = main.TRUE
152 for node in main.nodes:
153 secureSshResult = secureSshResult and main.ONOSbench.onosSecureSSH( node=node.ip_address )
154 utilities.assert_equals( expect=main.TRUE, actual=secureSshResult,
155 onpass="Test step PASS",
156 onfail="Test step FAIL" )
157
158 main.step( "Checking if ONOS is up yet" )
159 for i in range( 2 ):
160 onosIsupResult = main.TRUE
161 for node in main.nodes:
162 started = main.ONOSbench.isup( node.ip_address )
163 if not started:
164 main.log.error( node.name + " hasn't started" )
165 onosIsupResult = onosIsupResult and started
166 if onosIsupResult == main.TRUE:
167 break
168 utilities.assert_equals( expect=main.TRUE, actual=onosIsupResult,
169 onpass="ONOS startup successful",
170 onfail="ONOS startup failed" )
171
172 main.step( "Starting ONOS CLI sessions" )
173 cliResults = main.TRUE
174 threads = []
175 for i in range( main.numCtrls ):
176 t = main.Thread( target=main.CLIs[ i ].startOnosCli,
177 name="startOnosCli-" + str( i ),
178 args=[ main.nodes[ i ].ip_address ] )
179 threads.append( t )
180 t.start()
181
182 for t in threads:
183 t.join()
184 cliResults = cliResults and t.result
185 utilities.assert_equals( expect=main.TRUE, actual=cliResults,
186 onpass="ONOS cli startup successful",
187 onfail="ONOS cli startup failed" )
188
189 main.activeNodes = [ i for i in range( 0, len( main.CLIs ) ) ]
190
191 main.step( "Activate apps defined in the params file" )
192 # get data from the params
193 apps = main.params.get( 'apps' )
194 if apps:
195 apps = apps.split( ',' )
196 main.log.warn( apps )
197 activateResult = True
198 for app in apps:
199 main.CLIs[ 0 ].app( app, "Activate" )
200 # TODO: check this worked
201 time.sleep( SLEEP ) # wait for apps to activate
202 for app in apps:
203 state = main.CLIs[ 0 ].appStatus( app )
204 if state == "ACTIVE":
205 activateResult = activateResult and True
206 else:
207 main.log.error( "{} is in {} state".format( app, state ) )
208 activateResult = False
209 utilities.assert_equals( expect=True,
210 actual=activateResult,
211 onpass="Successfully activated apps",
212 onfail="Failed to activate apps" )
213 else:
214 main.log.warn( "No apps were specified to be loaded after startup" )
215
216 main.step( "Set ONOS configurations" )
217 config = main.params.get( 'ONOS_Configuration' )
218 if config:
219 main.log.debug( config )
220 checkResult = main.TRUE
221 for component in config:
222 for setting in config[ component ]:
223 value = config[ component ][ setting ]
224 check = main.CLIs[ 0 ].setCfg( component, setting, value )
225 main.log.info( "Value was changed? {}".format( main.TRUE == check ) )
226 checkResult = check and checkResult
227 utilities.assert_equals( expect=main.TRUE,
228 actual=checkResult,
229 onpass="Successfully set config",
230 onfail="Failed to set config" )
231 else:
232 main.log.warn( "No configurations were specified to be changed after startup" )
233
234 main.step( "App Ids check" )
235 appCheck = main.TRUE
236 threads = []
237 for i in main.activeNodes:
238 t = main.Thread( target=main.CLIs[ i ].appToIDCheck,
239 name="appToIDCheck-" + str( i ),
240 args=[] )
241 threads.append( t )
242 t.start()
243
244 for t in threads:
245 t.join()
246 appCheck = appCheck and t.result
247 if appCheck != main.TRUE:
248 main.log.warn( main.CLIs[ 0 ].apps() )
249 main.log.warn( main.CLIs[ 0 ].appIDs() )
250 utilities.assert_equals( expect=main.TRUE, actual=appCheck,
251 onpass="App Ids seem to be correct",
252 onfail="Something is wrong with app Ids" )
253
254 def CASE2( self, main ):
255 """
256 Load and test vpls configurations from json configuration file
257 """
258 import os.path
259 from tests.USECASE.VPLS.dependencies import vpls
260
261 main.vpls = vpls
262 pprint = main.ONOSrest1.pprint
263 hosts = int( main.params[ 'vpls' ][ 'hosts' ] )
264 SLEEP = int( main.params[ 'SLEEP' ][ 'netcfg' ] )
265
266 main.step( "Discover hosts using pings" )
267 for i in range( 1, hosts + 1 ):
268 src = "h" + str( i )
269 for j in range( 1, hosts + 1 ):
270 if j == i:
271 continue
272 dst = "h" + str( j )
273 pingResult = main.Mininet1.pingHost( SRC=src, TARGET=dst )
274
275 main.step( "Load VPLS configurations" )
276 fileName = main.params[ 'DEPENDENCY' ][ 'topology' ]
277 app = main.params[ 'vpls' ][ 'name' ]
278
279 loadVPLSResult = main.ONOSbench.onosNetCfg( main.nodes[ 0 ].ip_address, "", fileName )
280 utilities.assert_equals( expect=main.TRUE,
281 actual=loadVPLSResult,
282 onpass="Loaded vpls configuration.",
283 onfail="Failed to load vpls configuration.")
284
285 # Time for netcfg to load data
286 time.sleep( SLEEP )
287
288 main.step( "Check VPLS configurations" )
289 # 'Master' copy of test configuration
290 try:
291 with open( os.path.expanduser( fileName ) ) as dataFile:
292 originalCfg = json.load( dataFile )
293 main.vplsConfig = originalCfg[ 'apps' ].get( app ).get( 'vpls' ).get( 'vplsList' )
294 except Exception as e:
295 main.log.error( "Error loading config file: {}".format( e ) )
296 if main.vplsConfig:
297 result = True
298 else:
299 result = False
300 utilities.assert_equals( expect=True,
301 actual=result,
302 onpass="Check vpls configuration succeeded.",
303 onfail="Check vpls configuration failed." )
304
305 main.step( "Check interface configurations" )
306 result = False
307 getPorts = utilities.retry( f=main.ONOSrest1.getNetCfg,
308 retValue=False,
309 kwargs={"subjectClass":"ports"},
310 sleep=SLEEP )
311 onosCfg = pprint( getPorts )
312 sentCfg = pprint( originalCfg.get( "ports" ) )
313
314 if onosCfg == sentCfg:
315 main.log.info( "ONOS interfaces NetCfg matches what was sent" )
316 result = True
317 else:
318 main.log.error( "ONOS interfaces NetCfg doesn't match what was sent" )
319 main.log.debug( "ONOS config: {}".format( onosCfg ) )
320 main.log.debug( "Sent config: {}".format( sentCfg ) )
321 utilities.assert_equals( expect=True,
322 actual=result,
323 onpass="Net Cfg added for interfaces",
324 onfail="Net Cfg not added for interfaces" )
325
326 # Run a bunch of checks to verify functionality based on configs
327 vpls.verify( main )
328
329 # This is to avoid a race condition in pushing netcfg's.
330 main.step( "Loading vpls configuration in case any configuration was missed." )
331 loadVPLSResult = main.ONOSbench.onosNetCfg( main.nodes[ 0 ].ip_address, "", fileName )
332 utilities.assert_equals( expect=main.TRUE,
333 actual=loadVPLSResult,
334 onpass="Loaded vpls configuration.",
335 onfail="Failed to load vpls configuration." )
336
337
338
339 def CASE50( self, main ):
340 """
341 Initial connectivity check
342 """
343 main.case( "Check connectivity before running all other tests." )
344
345 # Check connectivity before running all other tests
346 connectCheckResult = main.vpls.testConnectivityVpls( main )
347 utilities.assert_equals( expect=main.TRUE, actual=connectCheckResult,
348 onpass="Connectivity is as expected.",
349 onfail="Connectivity is NOT as expected." )
350
351
352 def CASE100( self, main ):
353 """
354 Bring down 1 host at a time and test connectivity
355 """
356 assert vpls, "vpls not defined"
357
358 main.case( "Bring down one host at a time and test connectivity." )
359 result = main.TRUE
360
361 for i in range( 1, hosts + 1 ):
362
363 stri = str( i )
364
365 # Bring host down
366 main.step( "Kill link between s" + stri + " and h" + stri + "." )
367 linkDownResult = main.Mininet1.link( END1="s" + stri, END2="h" + stri, OPTION="down" )
368
369 # Check if link was successfully down'd
370 utilities.assert_equals( expect=main.TRUE, actual=linkDownResult,
371 onpass="Link down successful.",
372 onfail="Failed to bring link down." )
373 result = result and linkDownResult
374
375 # Check connectivity
376 connectivityResult = vpls.testConnectivityVpls( main, blacklist=[ "h" + stri ] )
377 result = result and connectivityResult
378
379 # Bring host up
380 main.step( "Re-adding link between s" + stri + " and h" + stri + "." )
381 linkUpResult = main.Mininet1.link( END1="s" + stri, END2="h" + stri, OPTION="up" )
382
383 # Check if link was successfully re-added
384 utilities.assert_equals( expect=main.TRUE, actual=linkUpResult,
385 onpass="Link up successful.",
386 onfail="Failed to bring link up." )
387 result = result and linkUpResult
388
389 # Discover host using ping
390 main.step( "Discover h" + stri + " using ping." )
391 main.Mininet1.pingHost( SRC="h" + stri, TARGET="h" + str( ( i % hosts ) + 1 ) )
392
393 # Check connectivity
394 connectivityResult = vpls.testConnectivityVpls( main )
395 result = result and connectivityResult
396
397 utilities.assert_equals( expect=main.TRUE, actual=result,
398 onpass="Connectivity is as expected.",
399 onfail="Connectivity is NOT as expected.")
400
401 def CASE200( self, main ):
402 """
403 Bring down 1 switch at a time and test connectivity
404 """
405 assert vpls, "vpls not defined"
406
407 main.case( "Bring down one switch at a time and test connectivity." )
408 links = main.Mininet1.getLinks( ) # Obtain links here
409 result = main.TRUE
410
411 for i in range( 5, hosts + 1 ):
412
413 stri = str( i )
414
415 # Bring switch down
416 main.step( "Delete s" + stri + ".")
417 delSwitchResult = main.Mininet1.delSwitch( sw="s" + stri )
418
419 # Check if switch was deleted
420 utilities.assert_equals( expect=main.TRUE, actual=delSwitchResult,
421 onpass="Successfully deleted switch.",
422 onfail="Failed to delete switch." )
423 result = result and delSwitchResult
424
425 # Check connectivity
426 connectivityResult = vpls.testConnectivityVpls( main, blacklist=[ "h" + stri ] )
427 result = result and connectivityResult
428
429 # Bring switch up
430 main.step( "Add s" + stri + ".")
431 addSwitchResult = main.Mininet1.addSwitch( sw="s" + stri )
432
433 # Check if switch was added
434 utilities.assert_equals( expect=main.TRUE, actual=addSwitchResult,
435 onpass="Successfully added switch.",
436 onfail="Failed to add switch.")
437 result = result and addSwitchResult
438
439 # Reconnect links
440 main.step( "Reconnecting links on s" + stri + ".")
441 for j in links:
442 if ( j[ 'node1' ] == "s" + stri and j[ 'node2' ][ 0 ] == "s" ) or \
443 ( j[ 'node2' ] == "s" + stri and j[ 'node1' ][ 0 ] == "s" ):
444 main.Mininet1.addLink( str( j[ 'node1' ] ), str( j[ 'node2' ] ) )
445
446 # Discover host using ping
447 main.Mininet1.pingHost( SRC="h" + stri, TARGET="h" + str( ( i % hosts ) + 1 ) )
448
449 # Check connectivity
450 connectivityResult = vpls.testConnectivityVpls( main )
451 result = result and connectivityResult
452
453
454 utilities.assert_equals( expect=main.TRUE,
455 actual=result,
456 onpass="Connectivity is as expected.",
457 onfail="Connectivity is NOT as expected." )
458
459 def CASE300( self, main ):
460 """
461 Stop 1 ONOS node at a time and test connectivity
462 """
463 from tests.USECASE.VPLS.dependencies import vpls
464 from tests.HA.dependencies.HA import HA
465 assert vpls, "vpls not defined"
466
467 main.HA = HA()
468 main.case( "Stop one ONOS node at a time and test connectivity." )
469
470 result = main.TRUE
471
472 for i in range( 0, len( main.nodes ) ):
473
474 stri = str( i )
475
476 ip_address = main.nodes[ i ].ip_address
477
478 # Stop an ONOS node: i
479 main.step( "Stop ONOS node " + stri + ".")
480 stopResult = main.ONOSbench.onosStop( ip_address )
481 main.activeNodes.remove( i )
482
483 utilities.assert_equals( expect=main.TRUE, actual=stopResult,
484 onpass="ONOS nodes stopped successfully.",
485 onfail="ONOS nodes NOT successfully stopped." )
486
487 # Check connectivity
488 connectivityResult = vpls.testConnectivityVpls( main, isNodeUp=False )
489 result = result and connectivityResult
490
491 # Restart ONOS node
492 main.step( "Restart ONOS node " + stri + " and checking status of restart.")
493 startResult = main.ONOSbench.onosStart( ip_address )
494
495 utilities.assert_equals( expect=main.TRUE, actual=startResult,
496 onpass="ONOS nodes started successfully.",
497 onfail="ONOS nodes NOT successfully started." )
498 result = result and startResult
499
500 # Check if ONOS is up yet
501 main.log.info( "Checking if ONOS node " + stri + " is up." )
502 upResult = main.ONOSbench.isup( ip_address )
503
504 utilities.assert_equals( expect=main.TRUE, actual=upResult,
505 onpass="ONOS successfully restarted.",
506 onfail="ONOS did NOT successfully restart." )
507
508 # Restart CLI
509 main.log.info( "Restarting ONOS node " + stri + "'s main.CLI." )
510 cliResult = main.CLIs[ i ].startOnosCli( ip_address )
511 main.activeNodes.append( i )
512
513 utilities.assert_equals( expect=main.TRUE, actual=cliResults,
514 onpass="ONOS CLI successfully restarted.",
515 onfail="ONOS CLI did NOT successfully restart." )
516
517 # Run some basic checks to see if ONOS node truly has succesfully restarted:
518
519 # Checking if all nodes appear with status READY using 'nodes' command
520 main.step( "Checking ONOS nodes." )
521 nodeResults = utilities.retry( main.HA.nodesCheck,
522 False,
523 args=[ main.activeNodes ],
524 sleep=main.timeSleep,
525 attempts=main.numAttempts )
526
527 utilities.assert_equals( expect=True, actual=nodeResults,
528 onpass="Nodes check successful.",
529 onfail="Nodes check NOT successful." )
530
531 # All apps that are present are active
532 main.log.info( "Checking if apps are active." )
533 compareAppsResult = vpls.compareApps( main )
534 utilities.assert_equals( expect=main.TRUE,
535 actual=compareAppsResult,
536 onpass="Apps are the same across all nodes.",
537 onfail="Apps are NOT the same across all nodes." )
538 result = result and compareAppsResult
539
540 # Check connectivity
541 connectivityResult = vpls.testConnectivityVpls( main )
542 result = result and connectivityResult
543
544 utilities.assert_equals( expect=main.TRUE,
545 actual=result,
546 onpass="Connectivity is as expected.",
547 onfail="Connectivity is NOT as expected." )
548
549
550 def CASE310( self, main ):
551 """
552 Kill 1 ONOS node at a time and test connectivity
553 """
554 assert vpls, "vpls not defined"
555
556 main.case( "Kill one ONOS node at a time and test connectivity." )
557 killSleep = int( main.params[ 'SLEEP' ][ 'killnode' ] )
558 result = main.TRUE
559
560 for i in range( 0, len( main.nodes ) ):
561
562 # Kill an ONOS node
563 main.step( "Killing ONOS node " + str( i + 1 ) + "." )
564 killresult = main.ONOSbench.onosKill( main.nodes[ i ].ip_address )
565
566 # Check if ONOS node has been successfully killed
567 utilities.assert_equals( expect=main.TRUE, actual=killresult,
568 onpass="ONOS node killed successfully.",
569 onfail="ONOS node NOT successfully killed." )
570
571 main.step( "Waiting for ONOS to restart." )
572 main.log.info( "Sleeping for " + str( killSleep ) + " seconds..." )
573 time.sleep( killSleep )
574
575 # Check connectivity
576 connectivityResult = vpls.testConnectivityVpls( main )
577 result = result and connectivityResult
578
579 utilities.assert_equals( expect=main.TRUE,
580 actual=result,
581 onpass="Connectivity is as expected.",
582 onfail="Connectivity is NOT as expected.")
583
584
585 def CASE400( self, main ):
586 """
587 Bring down 1 link at a time and test connectivity
588 """
589 assert vpls, "vpls not defined"
590
591
592 main.case( "Bring down one link at a time and test connectivity." )
593
594 result = main.TRUE
595
596 for link in main.Mininet1.getLinks():
597 nodes = [ link[ 'node1' ], link[ 'node2' ] ]
598
599 # Bring down a link
600 main.step( "Bring down link: " + nodes[ 0 ] + " to " + nodes[ 1 ] )
601 delLinkResult = main.Mininet1.link( END1=nodes[ 0 ], END2=nodes[ 1 ], OPTION="down" )
602
603 # Check if the link has successfully been brought down
604 utilities.assert_equals( expect=main.TRUE, actual=delLinkResult,
605 onpass="Successfully deleted link.",
606 onfail="Failed to delete link." )
607 result = result and delLinkResult
608
609 # Add removed host to blacklist if necessary
610 blacklist = []
611 for l in nodes:
612 if l[ 0 ] == 'h':
613 blacklist.append( l )
614
615 # Check intent states, then connectivity
616 connectivityResult = vpls.testConnectivityVpls( main, blacklist )
617 result = result and connectivityResult
618
619 # Re-add the link
620 main.step( "Adding link: " + nodes[ 0 ] + " to " + nodes[ 1 ] + "." )
621 addLinkResult = main.Mininet1.link( END1=nodes[ 0 ], END2=nodes[ 1 ], OPTION="up" )
622
623 # Check if the link has successfully been added
624 utilities.assert_equals( expect=main.TRUE, actual=addLinkResult,
625 onpass="Successfully added link.",
626 onfail="Failed to delete link." )
627 result = result and addLinkResult
628
629 main.log.debug( main.timeSleep )
630 time.sleep( main.timeSleep )
631
632 # Check intent states, then connectivity
633 connectivityResult = vpls.testConnectivityVpls( main )
634 result = result and connectivityResult
635
636 utilities.assert_equals( expect=main.TRUE,
637 actual=result,
638 onpass="Connectivity is as expected.",
639 onfail="Connectivity is NOT as expected." )