[ONOS-7625] Refactor SRMulticast for complex test scenarios

Change-Id: Id2a6a932523b06886d9f602c9bb720a7ce28e433
diff --git a/TestON/tests/USECASE/SegmentRouting/SRMulticast/dependencies/SRMulticastTest.py b/TestON/tests/USECASE/SegmentRouting/SRMulticast/dependencies/SRMulticastTest.py
index 42e646a..30b7e92 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRMulticast/dependencies/SRMulticastTest.py
+++ b/TestON/tests/USECASE/SegmentRouting/SRMulticast/dependencies/SRMulticastTest.py
@@ -20,96 +20,163 @@
 """
 
 import time
-from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as run
 
-class SRMulticastTest ():
+def setupTest( main, test_idx, onosNodes ):
+    from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as lib
+    skipPackage = False
+    init = False
+    if not hasattr( main, "apps" ):
+        init = True
+        lib.initTest( main )
+    # Skip onos packaging if the cluster size stays the same
+    if not init and onosNodes == main.Cluster.numCtrls:
+        skipPackage = True
 
-    def __init__( self ):
-        self.default = ''
-        self.switchNames = [ "leaf205", "leaf206", "spine227", "spine228" ]
+    main.resultFileName = "CASE%03d" % test_idx
+    main.Cluster.setRunningNode( onosNodes )
+    lib.installOnos( main, skipPackage=skipPackage, cliSleep=5 )
+    # Load configuration files
+    main.step( "Load configurations" )
+    main.cfgName = "TEST_CONFIG_ipv4=1_ipv6=1_dhcp=1_routers=1"
+    lib.loadJson( main )
+    time.sleep( float( main.params[ "timers" ][ "loadNetcfgSleep" ] ) )
+    main.cfgName = "common"
+    lib.loadMulticastConfig( main )
 
-    def runTest( self, main, test_idx, onosNodes, description, removeRoute=False, linkFailure=False, switchFailure=False ):
-        skipPackage = False
-        init = False
-        if not hasattr( main, 'apps' ):
-            init = True
-            run.initTest( main )
-        # Skip onos packaging if the cluster size stays the same
-        if not init and onosNodes == main.Cluster.numCtrls:
-            skipPackage = True
+    if hasattr( main, "Mininet1" ):
+        # Run the test with Mininet
+        mininet_args = " --dhcp=1 --routers=1 --ipv6=1 --ipv4=1"
+        lib.startMininet( main, main.params[ "DEPENDENCY" ][ "topology" ], args=mininet_args )
+        time.sleep( float( main.params[ "timers" ][ "startMininetSleep" ] ) )
+    else:
+        # Run the test with physical devices
+        lib.connectToPhysicalNetwork( main, self.switchNames )
+        # Check if the devices are up
+        lib.checkDevices( main, switches=len( self.switchNames ) )
 
-        main.case( '%s, ONOS cluster size: %s' % ( description, onosNodes ) )
+    # Create scapy components
+    lib.startScapyHosts( main )
 
-        main.resultFileName = 'CASE%03d' % test_idx
-        main.Cluster.setRunningNode( onosNodes )
-        run.installOnos( main, skipPackage=skipPackage, cliSleep=5 )
-        # Load configuration files
-        main.step("Load configurations")
-        main.cfgName = 'TEST_CONFIG_ipv4=1_ipv6=1_dhcp=1_routers=1'
-        run.loadJson( main )
-        main.cfgName = 'CASE%03d' % test_idx
-        run.loadMulticastConfig( main )
-        if linkFailure:
-            run.loadLinkFailureChart( main )
-        if switchFailure:
-            run.loadSwitchFailureChart( main )
-        time.sleep( float( main.params[ 'timers' ][ 'loadNetcfgSleep' ] ) )
+def verifyMcastRoutes( main ):
+    """
+    Install multicast routes and check traffic
+    """
+    from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as lib
+    for routeName in main.mcastRoutes.keys():
+        main.step( "Verify {} multicast route".format( routeName ) )
+        installMcastRoute( main, routeName )
+        lib.verifyMulticastTraffic( main, routeName, True )
 
-        if hasattr( main, 'Mininet1' ):
-            # Run the test with Mininet
-            mininet_args = ' --dhcp=1 --routers=1 --ipv6=1 --ipv4=1'
-            run.startMininet( main, main.params['DEPENDENCY']['topology'], args=mininet_args )
-            time.sleep( float( main.params[ 'timers' ][ 'startMininetSleep' ] ) )
-        else:
-            # Run the test with physical devices
-            run.connectToPhysicalNetwork( main, self.switchNames )
-            # Check if the devices are up
-            run.checkDevices( main, switches=len( self.switchNames ) )
+def installMcastRoute( main, routeName ):
+    """
+    Install a multicast route
+    """
+    routeData = main.multicastConfig[ routeName ]
+    src = main.mcastRoutes[ routeName ][ "src" ]
+    dst = main.mcastRoutes[ routeName ][ "dst" ]
+    main.Cluster.active( 0 ).CLI.mcastHostJoin( routeData[ "src" ][ src[ 0 ] ][ "ip" ], routeData[ "group" ],
+                                                [ routeData[ "src" ][ i ][ "port" ] for i in src ],
+                                                [ routeData[ "dst" ][ i ][ "id" ] for i in dst ] )
+    time.sleep( float( main.params[ "timers" ][ "mcastSleep" ] ) )
 
-        # Create scapy components
-        run.startScapyHosts( main )
+def verifyMcastRouteRemoval( main, routeName ):
+    """
+    Verify removal of a multicast route
+    """
+    routeData = main.multicastConfig[ routeName ]
+    main.step( "Verify removal of {} route".format( routeName ) )
+    main.Cluster.active( 0 ).CLI.mcastHostDelete( routeData[ "src" ][ 0 ][ "ip" ], routeData[ "group" ] )
+    # TODO: verify the deletion
 
-        for entry in main.multicastConfig:
-            main.step("Verify adding multicast route with group IP {}".format(entry["group"]))
-            # Create a multicast route
-            main.Cluster.active( 0 ).CLI.mcastHostJoin( entry["sIP"], entry["group"], entry["sPorts"], entry["dHosts"] )
-            time.sleep( float( main.params[ 'timers' ][ 'mcastSleep' ] ) )
-            # Check the flows against the devices
-            # run.checkFlows( main, minFlowCount=2, sleep=5 )
-            # Verify multicast traffic
-            run.verifyMulticastTraffic( main, entry, True, skipOnFail=True )
+def verifyMcastSinkRemoval( main, routeName, sinkIndex, expect ):
+    """
+    Verify removal of a multicast sink
+    """
+    from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as lib
+    routeData = main.multicastConfig[ routeName ]
+    sinkId = routeData[ "dst" ][ sinkIndex ][ "id" ]
+    main.step( "Verify removal of {} sink {}".format( routeName, sinkId ) )
+    main.Cluster.active( 0 ).CLI.mcastHostDelete( routeData[ "src" ][ 0 ][ "ip" ], routeData[ "group" ], sinkId )
+    time.sleep( float( main.params[ "timers" ][ "mcastSleep" ] ) )
+    lib.verifyMulticastTraffic( main, routeName, expect )
 
-            # Test switch failures
-            if switchFailure:
-                for switch, expected in main.switchFailureChart.items():
-                    run.killSwitch( main, switch, expected['switches_after_failure'], expected['links_after_failure'] )
-                    run.verifyMulticastTraffic( main, entry, True, skipOnFail=True )
+def verifyMcastSourceRemoval( main, routeName, sourceIndex, expect ):
+    """
+    Verify removal of a multicast source
+    """
+    from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as lib
+    routeData = main.multicastConfig[ routeName ]
+    sourcePort = [ routeData[ "src" ][ sourceIndex ][ "port" ] ]
+    main.step( "Verify removal of {} source {}".format( routeName, sourcePort ) )
+    main.Cluster.active( 0 ).CLI.mcastSourceDelete( routeData[ "src" ][ 0 ][ "ip" ], routeData[ "group" ], sourcePort )
+    time.sleep( float( main.params[ "timers" ][ "mcastSleep" ] ) )
+    lib.verifyMulticastTraffic( main, routeName, expect )
 
-                    run.recoverSwitch( main, switch, expected['switches_before_failure'], expected['links_before_failure'] )
-                    run.verifyMulticastTraffic( main, entry, True, skipOnFail=True )
+def verifyMcastRemoval( main, removeDHT1=True ):
+    """
+    Verify removal of IPv6 route, IPv4 sinks and IPv4 source
+    """
+    from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as lib
+    verifyMcastRouteRemoval( main, "ipv6" )
+    if removeDHT1:
+        verifyMcastSinkRemoval( main, "ipv4", 0, [ False, True, True ] )
+        verifyMcastSinkRemoval( main, "ipv4", 1, [ False, False, True ] )
+    else:
+        verifyMcastSinkRemoval( main, "ipv4", 2, [ True, True, False ] )
+        verifyMcastSinkRemoval( main, "ipv4", 1, [ True, False, False ] )
+    verifyMcastSourceRemoval( main, "ipv4", 0, False )
 
-            # Test link failures
-            if linkFailure:
-                for link_batch_name, info in main.linkFailureChart.items():
-                    linksToRemove = info['links'].values()
-                    linksBefore = info['links_before']
-                    linksAfter = info['links_after']
+def verifyLinkDown( main, link, affectedLinkNum, expectList={ "ipv4": True, "ipv6": True } ):
+    """
+    Kill a batch of links and verify traffic
+    Restore the links and verify traffic
+    """
+    from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as lib
+    link = link if ( isinstance( link, list ) and isinstance( link[ 0 ], list ) ) else [ link ]
+    # Kill the link(s)
+    lib.killLinkBatch( main, link, int( main.params[ "TOPO" ][ "linkNum" ] ) - affectedLinkNum, int( main.params[ "TOPO" ][ "switchNum" ] ) )
+    for routeName in expectList.keys():
+        lib.verifyMulticastTraffic( main, routeName, expectList[ routeName ] )
+    # Restore the link(s)
+    lib.restoreLinkBatch( main, link, int( main.params[ "TOPO" ][ "linkNum" ] ), int( main.params[ "TOPO" ][ "switchNum" ] ) )
+    for routeName in expectList.keys():
+        lib.verifyMulticastTraffic( main, routeName, True )
 
-                    run.killLinkBatch( main, linksToRemove, linksAfter, switches=10 )
-                    run.verifyMulticastTraffic( main, entry, True, skipOnFail=True )
+def verifySwitchDown( main, switchName, affectedLinkNum, expectList={ "ipv4": True, "ipv6": True } ):
+    """
+    Kill a batch of switches and verify traffic
+    Recover the swithces and verify traffic
+    """
+    from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as lib
+    switchName = switchName if isinstance( switchName, list ) else [ switchName ]
+    # Kill the switch(es)
+    lib.killSwitch( main, switchName, int( main.params[ "TOPO" ][ "switchNum" ] ) - len( switchName ), int( main.params[ "TOPO" ][ "linkNum" ] ) - affectedLinkNum )
+    for routeName in expectList.keys():
+        lib.verifyMulticastTraffic( main, routeName, expectList[ routeName ] )
+    # Recover the switch(es)
+    lib.recoverSwitch( main, switchName, int( main.params[ "TOPO" ][ "switchNum" ] ), int( main.params[ "TOPO" ][ "linkNum" ] ) )
+    for routeName in expectList.keys():
+        lib.verifyMulticastTraffic( main, routeName, True )
 
-                    run.restoreLinkBatch( main, linksToRemove, linksBefore, switches=10 )
-                    run.verifyMulticastTraffic( main, entry, True, skipOnFail=True )
-
-            if removeRoute:
-                main.step("Verify deleting multicast route with group IP {}".format(entry["group"]))
-                # delete a multicast route
-                main.Cluster.active( 0 ).CLI.mcastHostDelete( entry["sIP"], entry["group"] )
-                time.sleep( float( main.params[ 'timers' ][ 'mcastSleep' ] ) )
-                # Check the flows against the devices
-                # run.checkFlows( main, minFlowCount=2, sleep=5 )
-                # Verify multicast traffic (traffic check is expected to fail)
-                run.verifyMulticastTraffic( main, entry, False, skipOnFail=True )
-
-        # Clean up the environment
-        run.cleanup( main, copyKarafLog=False )
+def verifyOnosDown( main, expectList={ "ipv4": True, "ipv6": True } ):
+    """
+    Kill and recover ONOS instances Sequencially and check traffic
+    """
+    from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as lib
+    import json
+    numCtrls = len( main.Cluster.runningNodes )
+    links = len( json.loads( main.Cluster.next().links() ) )
+    switches = len( json.loads( main.Cluster.next().devices() ) )
+    for ctrl in xrange( numCtrls ):
+        # Kill node
+        lib.killOnos( main, [ ctrl ], switches, links, ( numCtrls - 1 ) )
+        main.Cluster.active(0).CLI.balanceMasters()
+        time.sleep( float( main.params[ 'timers' ][ 'balanceMasterSleep' ] ) )
+        for routeName in expectList.keys():
+            lib.verifyMulticastTraffic( main, routeName, True )
+        # Recover node
+        lib.recoverOnos( main, [ ctrl ], switches, links, numCtrls )
+        main.Cluster.active(0).CLI.balanceMasters()
+        time.sleep( float( main.params[ 'timers' ][ 'balanceMasterSleep' ] ) )
+        for routeName in expectList.keys():
+            lib.verifyMulticastTraffic( main, routeName, True )