Implement moveHost and SRRouting test CASE651

Change-Id: I9f7003bc07638ef4fd8f4a3960b0893cd78559e8
diff --git a/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.py b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.py
index 9c97ebb..f2b3b8a 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.py
+++ b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.py
@@ -863,3 +863,27 @@
                          int( main.params[ "TOPO" ][ "linkNum" ] ), 3 )
         verify( main )
         lib.cleanup( main, copyKarafLog=False, removeHostComponent=True )
+
+    def CASE651( self, main ):
+        """
+        Move a single-homed host from port A to port B in DAAS-1
+        Test connectivity (expect no failure)
+
+        Repeat with DAAS-2
+        """
+        import time
+        from tests.USECASE.SegmentRouting.SRRouting.dependencies.SRRoutingTest import *
+        from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as lib
+        main.case( "Move a single-homed host to another port in the same DAAS" )
+        setupTest( main, test_idx=651, onosNodes=3 )
+        main.Cluster.active( 0 ).CLI.balanceMasters()
+        time.sleep( float( main.params[ 'timers' ][ 'balanceMasterSleep' ] ) )
+        verify( main )
+
+        h1v4cfg = '{"of:0000000000000001/7" : { "interfaces" : [ { "ips" : [ "10.1.0.254/24" ], "vlan-untagged": 10 } ] } }'
+        lib.moveHost( main, "h1v4", "leaf1", "leaf1", "10.1.0.254", prefixLen=24, cfg=h1v4cfg )
+        verify( main )
+
+        h13v4cfg = '''{"of:0000000000000006/7" : { "interfaces" : [ { "ips" : [ "10.5.20.254/24" ], "vlan-untagged": 20 } ] } }'''
+        lib.moveHost( main, "h13v4", "leaf6", "leaf6", "10.5.20.254", prefixLen=24, cfg=h13v4cfg )
+        verify( main )
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py b/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
index 0daf074..01aa8f7 100644
--- a/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
@@ -1103,7 +1103,7 @@
                 # Send packet and check received packet
                 expectedResult = expect.pop( 0 ) if isinstance( expect, list ) else expect
                 t3Cmd = "t3-troubleshoot -vv -sp {} -et ipv{} -d {} -dm {}".format( srcEntry[ "port" ], routeData[ "ipVersion" ],
-                                                                                routeData[ "group" ], srcEntry[ "Ether" ] )
+                                                                                    routeData[ "group" ], srcEntry[ "Ether" ] )
                 trafficResult = main.topo.sendScapyPackets( sender, receiver, pktFilter, pkt, sIface, dIface,
                                                             expectedResult, maxRetry, True, t3Cmd )
                 if not trafficResult:
@@ -1174,3 +1174,48 @@
         utilities.assert_equals( expect=main.TRUE, actual=result,
                                  onpass="Location verification passed",
                                  onfail="Location verification failed" )
+
+    @staticmethod
+    def moveHost( main, hostName, srcSw, dstSw, gw, macAddr=None, prefixLen=None, cfg='', ipv6=False ):
+        """
+        Move specified host from srcSw to dstSw.
+        If srcSw and dstSw are same, the host will be moved from current port to
+        next available port.
+        Required:
+            hostName: name of the host. e.g., "h1"
+            srcSw: name of the switch that the host is attached to. e.g., "leaf1"
+            dstSw: name of the switch that the host will be moved to. e.g., "leaf2"
+            gw: ip address of the gateway of the new location
+        Optional:
+            macAddr: if specified, change MAC address of the host to the specified MAC address.
+            prefixLen: prefix length
+            cfg: port configuration as JSON string
+            ipv6: Use True to move IPv6 host
+        """
+
+        if not hasattr( main, 'Mininet1' ):
+            main.log.warn( "moveHost is supposed to be used only in Mininet." )
+            return
+
+        if ipv6:
+            main.Mininet1.moveHostv6( hostName, srcSw, dstSw, macAddr )
+        else:
+            main.Mininet1.moveHost( hostName, srcSw, dstSw, macAddr, prefixLen )
+
+        main.Mininet1.changeDefaultGateway( hostName, gw )
+        if cfg:
+            main.Cluster.active( 0 ).REST.setNetCfg( json.loads( cfg ),
+                                                     subjectClass="ports" )
+
+        main.Mininet1.discoverHosts( [ hostName, ] )
+
+        # Update expectedHost when MAC address is changed.
+        if macAddr is not None:
+            ipAddr = main.expectedHosts[ "network" ][ hostName ]
+            if ipAddr is not None:
+                for hostName, ip in main.expectedHosts[ "onos" ].items():
+                    if ip == ipAddr:
+                        vlan = hostName.split( "/" )[ -1 ]
+                        del main.expectedHosts[ "onos" ][ hostName ]
+                        main.expectedHosts[ "onos" ][ "{}/{}".format( macAddr, vlan ) ] = ip
+                        break