Merge "[ONOS-7676] Enable support for fabric.p4 in SegmentRouting ONOS System Tests"
diff --git a/TestON/drivers/common/cli/onosclidriver.py b/TestON/drivers/common/cli/onosclidriver.py
index f17b3f9..014154c 100755
--- a/TestON/drivers/common/cli/onosclidriver.py
+++ b/TestON/drivers/common/cli/onosclidriver.py
@@ -2817,7 +2817,7 @@
                 for l in rawFlows:
                     totalFlows += int( l.split( "Count=" )[ 1 ] )
             else:
-                main.log.error( "Response not as expected!" )
+                main.log.warn( "Response not as expected!" )
                 return None
             return totalFlows
 
diff --git a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params
index 6f9d60e..1520489 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params
+++ b/TestON/tests/USECASE/SegmentRouting/SRBridging/SRBridging.params
@@ -15,13 +15,14 @@
     <DEPENDENCY>
         <useCommonConf>False</useCommonConf>
         <useCommonTopo>True</useCommonTopo>
+        <useBmv2>False</useBmv2>
         <topology>trellis_fabric.py</topology>
         <lib>routinglib.py,trellislib.py</lib>
     </DEPENDENCY>
 
     <ENV>
         <cellName>productionCell</cellName>
-        <cellApps>drivers,segmentrouting,openflow,fpm,netcfghostprovider</cellApps>
+        <cellApps>drivers,segmentrouting,openflow,fpm,netcfghostprovider,drivers.bmv2,pipelines.fabric</cellApps>
     </ENV>
 
     <GIT>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRBridging/dependencies/SRBridgingTest.py b/TestON/tests/USECASE/SegmentRouting/SRBridging/dependencies/SRBridgingTest.py
index 9280ca8..af2848f 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRBridging/dependencies/SRBridgingTest.py
+++ b/TestON/tests/USECASE/SegmentRouting/SRBridging/dependencies/SRBridgingTest.py
@@ -20,19 +20,21 @@
 """
 
 from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as run
+import tests.USECASE.SegmentRouting.dependencies.cfgtranslator as translator
 
 class SRBridgingTest ():
 
     def __init__( self ):
         self.default = ''
         self.topo = dict()
-        # (number of spine switch, number of leaf switch, dual-homed, description, minFlowCount - leaf)
-        self.topo[ '0x1' ] = ( 0, 1, False, 'single ToR', 28 )
-        self.topo[ '0x2' ] = ( 0, 2, True, 'dual-homed ToR', 37 )
-        self.topo[ '2x2' ] = ( 2, 2, False, '2x2 leaf-spine topology', 37 )
+        # TODO: Check minFlowCount of leaf for BMv2 switch
+        # (number of spine switch, number of leaf switch, dual-homed, description, minFlowCount - leaf (OvS), minFlowCount - leaf (BMv2))
+        self.topo[ '0x1' ] = ( 0, 1, False, 'single ToR', 28, 28 )
+        self.topo[ '0x2' ] = ( 0, 2, True, 'dual-homed ToR', 37, 37 )
+        self.topo[ '2x2' ] = ( 2, 2, False, '2x2 leaf-spine topology', 37, 32 )
         # TODO: Implement 2x3 topology
         # topo[ '2x3' ] = ( 2, 3, True, '2x3 leaf-spine topology with dual ToR and single ToR', 28 )
-        self.topo[ '2x4' ] = ( 2, 4, True, '2x4 dual-homed leaf-spine topology', 53 )
+        self.topo[ '2x4' ] = ( 2, 4, True, '2x4 dual-homed leaf-spine topology', 53, 53 )
         self.switchNames = {}
         self.switchNames[ '2x2' ] = [ "leaf1", "leaf2", "spine101", "spine102" ]
 
@@ -52,6 +54,11 @@
         main.cfgName = 'CASE%01d%01d' % ( test_idx / 10, ( ( test_idx - 1 ) % 10 ) % 4 + 1 )
         main.Cluster.setRunningNode( onosNodes )
         run.installOnos( main, skipPackage=skipPackage, cliSleep=5 )
+        if main.useBmv2:
+            # Translate configuration file from OVS-OFDPA to BMv2 driver
+            translator.ofdpaToBmv2( main )
+        else:
+            translator.bmv2ToOfdpa( main )
         run.loadJson( main )
         run.loadChart( main )
         if hasattr( main, 'Mininet1' ):
@@ -61,16 +68,22 @@
                 mininet_args += ' --dual-homed'
             if len( vlan ) > 0 :
                 mininet_args += ' --vlan=%s' % ( ','.join( ['%d' % vlanId for vlanId in vlan ] ) )
+            if main.useBmv2:
+                mininet_args += ' --switch bmv2'
+                main.log.info( "Using BMv2 switch" )
 
             run.startMininet( main, 'trellis_fabric.py', args=mininet_args )
         else:
             # Run the test with physical devices
             run.connectToPhysicalNetwork( main, self.switchNames[ topology ] )
 
-        run.checkFlows( main, minFlowCount=self.topo[ topology ][ 4 ] * self.topo[ topology ][ 1 ], sleep=5 )
-        leaf_dpid = [ "of:%016d" % ( ls + 1 ) for ls in range( self.topo[ topology ][ 1 ] ) ]
+        run.checkFlows( main, minFlowCount=self.topo[ topology ][ 5 if main.useBmv2 else 4 ] * self.topo[ topology ][ 1 ], sleep=5 )
+        if main.useBmv2:
+            leaf_dpid = [ "device:bmv2:leaf%d" % ( ls + 1 ) for ls in range( self.topo[ topology ][ 1 ]) ]
+        else:
+            leaf_dpid = [ "of:%016d" % ( ls + 1 ) for ls in range( self.topo[ topology ][ 1 ] ) ]
         for dpid in leaf_dpid:
-            run.checkFlowsByDpid( main, dpid, self.topo[ topology ][ 4 ], sleep=5 )
+            run.checkFlowsByDpid( main, dpid, self.topo[ topology ][ 5 if main.useBmv2 else 4 ], sleep=5 )
         run.pingAll( main )
 
         run.cleanup( main )
diff --git a/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params b/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params
index e337e12..6d1ae4a 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params
+++ b/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/SRDhcprelay.params
@@ -14,6 +14,7 @@
     <DEPENDENCY>
         <useCommonConf>False</useCommonConf>
         <useCommonTopo>True</useCommonTopo>
+        <useBmv2>False</useBmv2>
         <topology>trellis_fabric.py</topology>
         <lib>routinglib.py,trellislib.py</lib>
         <conf>dhcpd.conf,dhcpd6.conf,bgpdr1.conf,bgpdr2.conf,bgpdbgp1.conf,zebradbgp1.conf,bgpdbgp2.conf,zebradbgp2.conf</conf>
@@ -21,7 +22,7 @@
 
     <ENV>
         <cellName>productionCell</cellName>
-        <cellApps>drivers,openflow,segmentrouting,fpm,dhcprelay,netcfghostprovider,routeradvertisement</cellApps>
+        <cellApps>drivers,openflow,segmentrouting,fpm,dhcprelay,netcfghostprovider,routeradvertisement,drivers.bmv2,pipelines.fabric</cellApps>
     </ENV>
 
     <GIT>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/dependencies/SRDhcprelayTest.py b/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/dependencies/SRDhcprelayTest.py
index 9693add..46220de 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/dependencies/SRDhcprelayTest.py
+++ b/TestON/tests/USECASE/SegmentRouting/SRDhcprelay/dependencies/SRDhcprelayTest.py
@@ -20,6 +20,7 @@
 """
 
 from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as run
+import tests.USECASE.SegmentRouting.dependencies.cfgtranslator as translator
 
 class SRDhcprelayTest ():
 
@@ -44,6 +45,11 @@
         main.resultFileName = 'CASE%02d' % testIndex
         main.Cluster.setRunningNode( onosNodes )
         run.installOnos( main, skipPackage=skipPackage, cliSleep=5 )
+        if main.useBmv2:
+            # Translate configuration file from OVS-OFDPA to BMv2 driver
+            translator.ofdpaToBmv2( main )
+        else:
+            translator.bmv2ToOfdpa( main )
         run.loadJson( main )
         run.loadHost( main )
         if hasattr( main, 'Mininet1' ):
@@ -63,6 +69,9 @@
                 mininet_args += ' --ipv6'
             if len( vlan ) > 0 :
                 mininet_args += ' --vlan=%s' % ( ','.join( ['%d' % vlanId for vlanId in vlan ] ) )
+            if main.useBmv2:
+                mininet_args += ' --switch bmv2'
+                main.log.info( "Using BMv2 switch" )
 
             run.startMininet( main, 'trellis_fabric.py', args=mininet_args )
         else:
diff --git a/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/SRDynamicConf.params b/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/SRDynamicConf.params
index fa26772..284e1e3 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/SRDynamicConf.params
+++ b/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/SRDynamicConf.params
@@ -14,13 +14,14 @@
     <DEPENDENCY>
         <useCommonConf>False</useCommonConf>
         <useCommonTopo>True</useCommonTopo>
+        <useBmv2>False</useBmv2>
         <topology>trellis_fabric.py</topology>
         <lib>routinglib.py,trellislib.py</lib>
     </DEPENDENCY>
 
     <ENV>
         <cellName>productionCell</cellName>
-        <cellApps>drivers,segmentrouting,openflow,fpm,netcfghostprovider</cellApps>
+        <cellApps>drivers,segmentrouting,openflow,fpm,netcfghostprovider,drivers.bmv2,pipelines.fabric</cellApps>
     </ENV>
 
     <GIT>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/dependencies/SRDynamicConfTest.py b/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/dependencies/SRDynamicConfTest.py
index 0341046..4e1703e 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/dependencies/SRDynamicConfTest.py
+++ b/TestON/tests/USECASE/SegmentRouting/SRDynamicConf/dependencies/SRDynamicConfTest.py
@@ -19,6 +19,8 @@
     along with TestON.  If not, see <http://www.gnu.org/licenses/>.
 """
 
+import tests.USECASE.SegmentRouting.dependencies.cfgtranslator as translator
+
 class SRDynamicConfTest:
     def __init__( self ):
         self.default = ''
@@ -69,17 +71,34 @@
 
         # Provide common configuration
         # TODO: Generate json and chart dynamically, according to topologies and scenarios
+        if main.useBmv2:
+            # Translate configuration file from OVS-OFDPA to BMv2 driver
+            translator.ofdpaToBmv2( main )
+        else:
+            translator.bmv2ToOfdpa( main )
         run.loadJson( main )
         run.loadChart( main )
 
         # Provide topology-specific interface configuration
         import json
         try:
-            with open( "%s%s%s.json" % (main.configPath, main.forJson, TAG) ) as cfg:
+            intfCfg = "%s%s%s.json" % ( main.configPath, main.forJson, TAG )
+            if main.useBmv2:
+                # Translate configuration file from OVS-OFDPA to BMv2 driver
+                translator.ofdpaToBmv2( main, intfCfg )
+            else:
+                translator.bmv2ToOfdpa( main, intfCfg )
+            with open( intfCfg ) as cfg:
                 main.Cluster.active( 0 ).REST.setNetCfg( json.load( cfg ) )
         except IOError:
             # Load default interface configuration
-            with open( "%s%s%s_ports.json" % (main.configPath, main.forJson, topology) ) as cfg:
+            defaultIntfCfg = "%s%s%s_ports.json" % ( main.configPath, main.forJson, topology )
+            if main.useBmv2:
+                # Translate configuration file from OVS-OFDPA to BMv2 driver
+                translator.ofdpaToBmv2( main, defaultIntfCfg )
+            else:
+                translator.bmv2ToOfdpa( main, defaultIntfCfg )
+            with open( defaultIntfCfg ) as cfg:
                 main.Cluster.active( 0 ).REST.setNetCfg( json.load( cfg ) )
 
         try:
@@ -101,6 +120,9 @@
                     mininet_args += ',0,0,0,0'
             if dualHomed:
                 mininet_args += ' --dual-homed'
+            if main.useBmv2:
+                mininet_args += ' --switch bmv2'
+                main.log.info( "Using BMv2 switch" )
 
             run.startMininet( main, 'trellis_fabric.py', args=mininet_args )
         else:
@@ -113,7 +135,10 @@
         # Check connectivity before changing interface configuration
         run.pingAll( main, '%s_Before' % TAG, retryAttempts=2 )
 
-        leaf_dpid = [ "of:%016d" % ( ls + 1 ) for ls in range( topo[ topology ][ 1 ] ) ]
+        if main.useBmv2:
+            leaf_dpid = [ "device:bmv2:leaf%d" % ( ls + 1 ) for ls in range( topo[ topology ][ 1 ] ) ]
+        else:
+            leaf_dpid = [ "of:%016d" % ( ls + 1 ) for ls in range( topo[ topology ][ 1 ] ) ]
         for dpid in leaf_dpid:
             run.checkFlowsByDpid( main, dpid, minFlowCountPerLeaf, sleep=5 )
 
@@ -347,8 +372,16 @@
     @staticmethod
     def updateIntfCfg( main, portNum, dualHomed, ips=[], untagged=0, tagged=[], native=0 ):
         from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as run
-        run.updateIntfCfg( main, "of:0000000000000001/%d" % portNum,
-                           ips=ips, untagged=untagged, tagged=tagged, native=native )
-        if dualHomed:
-            run.updateIntfCfg( main, "of:0000000000000002/%d" % portNum,
+        if main.useBmv2:
+            run.updateIntfCfg( main, "device:bmv2:leaf1/%d" % portNum,
                                ips=ips, untagged=untagged, tagged=tagged, native=native )
+        else:
+            run.updateIntfCfg( main, "of:0000000000000001/%d" % portNum,
+                               ips=ips, untagged=untagged, tagged=tagged, native=native )
+        if dualHomed:
+            if main.useBmv2:
+                run.updateIntfCfg( main, "device:bmv2:leaf2/%d" % portNum,
+                                   ips=ips, untagged=untagged, tagged=tagged, native=native )
+            else:
+                run.updateIntfCfg( main, "of:0000000000000002/%d" % portNum,
+                                   ips=ips, untagged=untagged, tagged=tagged, native=native )
diff --git a/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.params b/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.params
index 14b0977..7ee65f6 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.params
+++ b/TestON/tests/USECASE/SegmentRouting/SRMulticast/SRMulticast.params
@@ -14,6 +14,7 @@
     <DEPENDENCY>
         <useCommonConf>False</useCommonConf>
         <useCommonTopo>True</useCommonTopo>
+        <useBmv2>False</useBmv2>
         <topology>hagg_fabric.py</topology>
         <lib>routinglib.py,trellislib.py</lib>
         <conf>bgpdbgp1.conf,bgpdbgp2.conf,bgpdr1.conf,bgpdr2.conf,dhcpd6.conf,dhcpd.conf,zebradbgp1.conf,zebradbgp2.conf</conf>
@@ -21,7 +22,7 @@
 
     <ENV>
         <cellName>productionCell</cellName>
-        <cellApps>drivers,segmentrouting,openflow,fpm,dhcprelay,netcfghostprovider,routeradvertisement,t3,mcast,hostprobingprovider</cellApps>
+        <cellApps>drivers,segmentrouting,openflow,fpm,dhcprelay,netcfghostprovider,routeradvertisement,t3,mcast,hostprobingprovider,drivers.bmv2,pipelines.fabric</cellApps>
     </ENV>
 
     <GIT>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRMulticast/dependencies/SRMulticastTest.py b/TestON/tests/USECASE/SegmentRouting/SRMulticast/dependencies/SRMulticastTest.py
index 33e9656..39897d5 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRMulticast/dependencies/SRMulticastTest.py
+++ b/TestON/tests/USECASE/SegmentRouting/SRMulticast/dependencies/SRMulticastTest.py
@@ -23,6 +23,8 @@
 
 def setupTest( main, test_idx, onosNodes ):
     from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as lib
+    import tests.USECASE.SegmentRouting.dependencies.cfgtranslator as translator
+
     skipPackage = False
     init = False
     if not hasattr( main, "apps" ):
@@ -38,6 +40,11 @@
     # Load configuration files
     main.step( "Load configurations" )
     main.cfgName = "TEST_CONFIG_ipv4=1_ipv6=1" if hasattr( main, "Mininet1" ) else main.params[ "DEPENDENCY" ][ "confName" ]
+    if main.useBmv2:
+        # Translate configuration file from OVS-OFDPA to BMv2 driver
+        translator.ofdpaToBmv2( main )
+    else:
+        translator.bmv2ToOfdpa( main )
     lib.loadJson( main )
     time.sleep( float( main.params[ "timers" ][ "loadNetcfgSleep" ] ) )
     main.cfgName = "common" if hasattr( main, "Mininet1" ) else main.params[ "DEPENDENCY" ][ "confName" ]
@@ -47,6 +54,9 @@
     if hasattr( main, "Mininet1" ):
         # Run the test with Mininet
         mininet_args = " --dhcp=1 --routers=1 --ipv6=1 --ipv4=1"
+        if main.useBmv2:
+            mininet_args += ' --switch bmv2'
+            main.log.info( "Using BMv2 switch" )
         lib.startMininet( main, main.params[ "DEPENDENCY" ][ "topology" ], args=mininet_args )
         time.sleep( float( main.params[ "timers" ][ "startMininetSleep" ] ) )
     else:
diff --git a/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params
index 1b86dcb..88e5736 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params
+++ b/TestON/tests/USECASE/SegmentRouting/SRRouting/SRRouting.params
@@ -14,6 +14,7 @@
     <DEPENDENCY>
         <useCommonConf>False</useCommonConf>
         <useCommonTopo>True</useCommonTopo>
+        <useBmv2>False</useBmv2>
         <topology>hagg_fabric.py</topology>
         <lib>routinglib.py,trellislib.py,trellis_fabric.py</lib>
         <conf>bgpdbgp1.conf,bgpdbgp2.conf,bgpdr1.conf,bgpdr2.conf,dhcpd6.conf,dhcpd.conf,zebradbgp1.conf,zebradbgp2.conf</conf>
@@ -21,7 +22,7 @@
 
     <ENV>
         <cellName>productionCell</cellName>
-        <cellApps>drivers,openflow,segmentrouting,fpm,dhcprelay,netcfghostprovider,routeradvertisement,t3,hostprobingprovider</cellApps>
+        <cellApps>drivers,openflow,segmentrouting,fpm,dhcprelay,netcfghostprovider,routeradvertisement,t3,hostprobingprovider,drivers.bmv2,pipelines.fabric</cellApps>
     </ENV>
 
     <GIT>
diff --git a/TestON/tests/USECASE/SegmentRouting/SRRouting/dependencies/SRRoutingTest.py b/TestON/tests/USECASE/SegmentRouting/SRRouting/dependencies/SRRoutingTest.py
index 479b3f4..0b13a61 100644
--- a/TestON/tests/USECASE/SegmentRouting/SRRouting/dependencies/SRRoutingTest.py
+++ b/TestON/tests/USECASE/SegmentRouting/SRRouting/dependencies/SRRoutingTest.py
@@ -25,6 +25,7 @@
     SRRouting test setup
     """
     from tests.USECASE.SegmentRouting.dependencies.Testcaselib import Testcaselib as lib
+    import tests.USECASE.SegmentRouting.dependencies.cfgtranslator as translator
     import time
 
     skipPackage = False
@@ -61,6 +62,11 @@
                                                              1 if ipv6 else 0)
     else:
         main.cfgName = main.params[ "DEPENDENCY" ][ "confName" ]
+    if main.useBmv2:
+        # Translate configuration file from OVS-OFDPA to BMv2 driver
+        translator.ofdpaToBmv2( main )
+    else:
+        translator.bmv2ToOfdpa( main )
     lib.loadJson( main )
     time.sleep( float( main.params[ 'timers' ][ 'loadNetcfgSleep' ] ) )
     lib.loadHost( main )
@@ -81,6 +87,9 @@
         # Run the test with Mininet
         mininet_args = ' --dhcp=1 --routers=1 --ipv6={} --ipv4={}'.format( 1 if ipv6 else 0,
                                                                            1 if ipv4 else 0 )
+        if main.useBmv2:
+            mininet_args += ' --switch bmv2'
+            main.log.info( "Using BMv2 switch" )
         lib.startMininet( main, main.params[ 'DEPENDENCY' ][ 'topology' ], args=mininet_args )
         time.sleep( float( main.params[ "timers" ][ "startMininetSleep" ] ) )
     else:
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py b/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
index f7b9d2e..313d9b6 100644
--- a/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/Testcaselib.py
@@ -59,7 +59,12 @@
             main.useCommonTopo = main.params[ 'DEPENDENCY' ][ 'useCommonTopo' ] == 'True'
             main.topoPath = main.path + ( "/.." if main.useCommonTopo else "" ) + "/dependencies/"
             main.useCommonConf = main.params[ 'DEPENDENCY' ][ 'useCommonConf' ] == 'True'
+            if main.params[ 'DEPENDENCY' ].get( 'useBmv2' ):
+                main.useBmv2 = main.params[ 'DEPENDENCY' ][ 'useBmv2' ] == 'True'
+            else:
+                main.useBmv2 = False
             main.configPath = main.path + ( "/.." if main.useCommonConf else "" ) + "/dependencies/"
+            main.bmv2Path = main.path + "/../dependencies/"
             main.forJson = "json/"
             main.forChart = "chart/"
             main.forConfig = "conf/"
@@ -70,6 +75,7 @@
             main.topology = main.params[ 'DEPENDENCY' ][ 'topology' ]
             main.topologyLib = main.params[ 'DEPENDENCY' ][ 'lib' ] if 'lib' in main.params[ 'DEPENDENCY' ] else None
             main.topologyConf = main.params[ 'DEPENDENCY' ][ 'conf' ] if 'conf' in main.params[ 'DEPENDENCY' ] else None
+            main.bmv2 = "bmv2.py"
             main.scale = ( main.params[ 'SCALE' ][ 'size' ] ).split( "," )
             main.maxNodes = int( main.params[ 'SCALE' ][ 'max' ] )
 
@@ -200,6 +206,10 @@
                                                                 main.configPath + main.forConfig + conf,
                                                                 "~/",
                                                                 direction="to" )
+        copyResult = copyResult and main.ONOSbench.scp( main.Mininet1,
+                                                        main.bmv2Path + main.bmv2,
+                                                        main.Mininet1.home + "custom",
+                                                        direction="to" )
         stepResult = copyResult
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/bmv2.py b/TestON/tests/USECASE/SegmentRouting/dependencies/bmv2.py
new file mode 100644
index 0000000..eafb482
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/bmv2.py
@@ -0,0 +1,353 @@
+import json
+import multiprocessing
+import os
+import random
+import re
+import socket
+import threading
+import urllib2
+from contextlib import closing
+
+import time
+from mininet.log import info, warn
+from mininet.node import Switch, Host
+
+SIMPLE_SWITCH_GRPC = 'simple_switch_grpc'
+PKT_BYTES_TO_DUMP = 80
+VALGRIND_PREFIX = 'valgrind --leak-check=yes'
+SWITCH_START_TIMEOUT = 5  # seconds
+BMV2_LOG_LINES = 5
+BMV2_DEFAULT_DEVICE_ID = 1
+
+
+def parseBoolean(value):
+    if value in ['1', 1, 'true', 'True']:
+        return True
+    else:
+        return False
+
+
+def pickUnusedPort():
+    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+    s.bind(('localhost', 0))
+    addr, port = s.getsockname()
+    s.close()
+    return port
+
+
+def writeToFile(path, value):
+    with open(path, "w") as f:
+        f.write(str(value))
+
+
+def watchDog(sw):
+    while True:
+        if ONOSBmv2Switch.mininet_exception == 1:
+            sw.killBmv2(log=False)
+            return
+        if sw.stopped:
+            return
+        with closing(socket.socket(socket.AF_INET, socket.SOCK_STREAM)) as s:
+            if s.connect_ex(('127.0.0.1', sw.grpcPort)) == 0:
+                time.sleep(1)
+            else:
+                warn("\n*** WARN: BMv2 instance %s died!\n" % sw.name)
+                sw.printBmv2Log()
+                print ("-" * 80) + "\n"
+                return
+
+
+class ONOSHost(Host):
+    def __init__(self, name, inNamespace=True, **params):
+        Host.__init__(self, name, inNamespace=inNamespace, **params)
+
+    def config(self, **params):
+        r = super(Host, self).config(**params)
+        for off in ["rx", "tx", "sg"]:
+            cmd = "/sbin/ethtool --offload %s %s off" \
+                  % (self.defaultIntf(), off)
+            self.cmd(cmd)
+        # disable IPv6
+        self.cmd("sysctl -w net.ipv6.conf.all.disable_ipv6=1")
+        self.cmd("sysctl -w net.ipv6.conf.default.disable_ipv6=1")
+        self.cmd("sysctl -w net.ipv6.conf.lo.disable_ipv6=1")
+        return r
+
+
+class ONOSBmv2Switch(Switch):
+    """BMv2 software switch with gRPC server"""
+    # Shared value used to notify to all instances of this class that a Mininet
+    # exception occurred. Mininet exception handling doesn't call the stop()
+    # method, so the mn process would hang after clean-up since Bmv2 would still
+    # be running.
+    mininet_exception = multiprocessing.Value('i', 0)
+
+    def __init__(self, name, json=None, debugger=False, loglevel="warn",
+                 elogger=False, grpcport=None, cpuport=255, notifications=False,
+                 thriftport=None, netcfg=True, dryrun=False, pipeconf="",
+                 pktdump=False, valgrind=False, gnmi=False,
+                 portcfg=True, onosdevid=None, **kwargs):
+        Switch.__init__(self, name, **kwargs)
+        self.grpcPort = grpcport
+        self.thriftPort = thriftport
+        self.cpuPort = cpuport
+        self.json = json
+        self.debugger = parseBoolean(debugger)
+        self.notifications = parseBoolean(notifications)
+        self.loglevel = loglevel
+        # Important: Mininet removes all /tmp/*.log files in case of exceptions.
+        # We want to be able to see the bmv2 log if anything goes wrong, hence
+        # avoid the .log extension.
+        self.logfile = '/tmp/bmv2-%s-log' % self.name
+        self.elogger = parseBoolean(elogger)
+        self.pktdump = parseBoolean(pktdump)
+        self.netcfg = parseBoolean(netcfg)
+        self.dryrun = parseBoolean(dryrun)
+        self.valgrind = parseBoolean(valgrind)
+        self.netcfgfile = '/tmp/bmv2-%s-netcfg.json' % self.name
+        self.pipeconfId = pipeconf
+        self.injectPorts = parseBoolean(portcfg)
+        self.withGnmi = parseBoolean(gnmi)
+        self.longitude = kwargs['longitude'] if 'longitude' in kwargs else None
+        self.latitude = kwargs['latitude'] if 'latitude' in kwargs else None
+        if onosdevid is not None and len(onosdevid) > 0:
+            self.onosDeviceId = onosdevid
+        else:
+            self.onosDeviceId = "device:bmv2:%s" % self.name
+        self.logfd = None
+        self.bmv2popen = None
+        self.stopped = False
+
+        # Remove files from previous executions
+        self.cleanupTmpFiles()
+
+    def getSourceIp(self, dstIP):
+        """
+        Queries the Linux routing table to get the source IP that can talk with
+        dstIP, and vice versa.
+        """
+        ipRouteOut = self.cmd('ip route get %s' % dstIP)
+        r = re.search(r"src (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})", ipRouteOut)
+        return r.group(1) if r else None
+
+    def getDeviceConfig(self, srcIP):
+
+        basicCfg = {
+            "driver": "bmv2"
+        }
+
+        if self.longitude and self.latitude:
+            basicCfg["longitude"] = self.longitude
+            basicCfg["latitude"] = self.latitude
+
+        cfgData = {
+            "generalprovider": {
+                "p4runtime": {
+                    "ip": srcIP,
+                    "port": self.grpcPort,
+                    "deviceId": BMV2_DEFAULT_DEVICE_ID,
+                    "deviceKeyId": "p4runtime:%s" % self.onosDeviceId
+                },
+                "bmv2-thrift": {
+                    "ip": srcIP,
+                    "port": self.thriftPort
+                }
+            },
+            "piPipeconf": {
+                "piPipeconfId": self.pipeconfId
+            },
+            "basic": basicCfg
+        }
+
+        if self.withGnmi:
+            cfgData["generalprovider"]["gnmi"] = {
+                "ip": srcIP,
+                "port": self.grpcPort
+            }
+
+        if self.injectPorts:
+            portData = {}
+            portId = 1
+            for intfName in self.intfNames():
+                if intfName == 'lo':
+                    continue
+                portData[str(portId)] = {
+                    "number": portId,
+                    "name": intfName,
+                    "enabled": True,
+                    "removed": False,
+                    "type": "copper",
+                    "speed": 10000
+                }
+                portId += 1
+
+            cfgData['ports'] = portData
+
+        return cfgData
+
+    def doOnosNetcfg(self, controllerIP):
+        """
+        Notifies ONOS about the new device via Netcfg.
+        """
+        srcIP = self.getSourceIp(controllerIP)
+        if not srcIP:
+            warn("*** WARN: unable to get switch IP address, won't do netcfg\n")
+            return
+
+        cfgData = {
+            "devices": {
+                self.onosDeviceId: self.getDeviceConfig(srcIP)
+            }
+        }
+        with open(self.netcfgfile, 'w') as fp:
+            json.dump(cfgData, fp, indent=4)
+
+        if not self.netcfg:
+            # Do not push config to ONOS.
+            return
+
+        # Build netcfg URL
+        url = 'http://%s:8181/onos/v1/network/configuration/' % controllerIP
+        # Instantiate password manager for HTTP auth
+        pm = urllib2.HTTPPasswordMgrWithDefaultRealm()
+        pm.add_password(None, url,
+                        os.environ['ONOS_WEB_USER'],
+                        os.environ['ONOS_WEB_PASS'])
+        urllib2.install_opener(urllib2.build_opener(
+            urllib2.HTTPBasicAuthHandler(pm)))
+        # Push config data to controller
+        req = urllib2.Request(url, json.dumps(cfgData),
+                              {'Content-Type': 'application/json'})
+        try:
+            f = urllib2.urlopen(req)
+            print f.read()
+            f.close()
+        except urllib2.URLError as e:
+            warn("*** WARN: unable to push config to ONOS (%s)\n" % e.reason)
+
+    def start(self, controllers):
+        bmv2Args = [SIMPLE_SWITCH_GRPC] + self.grpcTargetArgs()
+        if self.valgrind:
+            bmv2Args = VALGRIND_PREFIX.split() + bmv2Args
+
+        cmdString = " ".join(bmv2Args)
+
+        if self.dryrun:
+            info("\n*** DRY RUN (not executing bmv2)")
+
+        info("\nStarting BMv2 target: %s\n" % cmdString)
+
+        writeToFile("/tmp/bmv2-%s-grpc-port" % self.name, self.grpcPort)
+        writeToFile("/tmp/bmv2-%s-thrift-port" % self.name, self.thriftPort)
+
+        try:
+            if not self.dryrun:
+                # Start the switch
+                self.logfd = open(self.logfile, "w")
+                self.bmv2popen = self.popen(cmdString,
+                                            stdout=self.logfd,
+                                            stderr=self.logfd)
+                self.waitBmv2Start()
+                # We want to be notified if BMv2 dies...
+                threading.Thread(target=watchDog, args=[self]).start()
+
+            self.doOnosNetcfg(self.controllerIp(controllers))
+        except Exception:
+            ONOSBmv2Switch.mininet_exception = 1
+            self.killBmv2()
+            self.printBmv2Log()
+            raise
+
+    def grpcTargetArgs(self):
+        if self.grpcPort is None:
+            self.grpcPort = pickUnusedPort()
+        if self.thriftPort is None:
+            self.thriftPort = pickUnusedPort()
+        args = ['--device-id %s' % str(BMV2_DEFAULT_DEVICE_ID)]
+        for port, intf in self.intfs.items():
+            if not intf.IP():
+                args.append('-i %d@%s' % (port, intf.name))
+        args.append('--thrift-port %s' % self.thriftPort)
+        if self.notifications:
+            ntfaddr = 'ipc:///tmp/bmv2-%s-notifications.ipc' % self.name
+            args.append('--notifications-addr %s' % ntfaddr)
+        if self.elogger:
+            nanologaddr = 'ipc:///tmp/bmv2-%s-nanolog.ipc' % self.name
+            args.append('--nanolog %s' % nanologaddr)
+        if self.debugger:
+            dbgaddr = 'ipc:///tmp/bmv2-%s-debug.ipc' % self.name
+            args.append('--debugger-addr %s' % dbgaddr)
+        args.append('--log-console')
+        if self.pktdump:
+            args.append('--pcap --dump-packet-data %s' % PKT_BYTES_TO_DUMP)
+        args.append('-L%s' % self.loglevel)
+        if not self.json:
+            args.append('--no-p4')
+        else:
+            args.append(self.json)
+        # gRPC target-specific options
+        args.append('--')
+        args.append('--cpu-port %s' % self.cpuPort)
+        args.append('--grpc-server-addr 0.0.0.0:%s' % self.grpcPort)
+        return args
+
+    def waitBmv2Start(self):
+        # Wait for switch to open gRPC port, before sending ONOS the netcfg.
+        # Include time-out just in case something hangs.
+        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        endtime = time.time() + SWITCH_START_TIMEOUT
+        while True:
+            result = sock.connect_ex(('127.0.0.1', self.grpcPort))
+            if result == 0:
+                # The port is open. Let's go! (Close socket first)
+                sock.close()
+                break
+            # Port is not open yet. If there is time, we wait a bit.
+            if endtime > time.time():
+                time.sleep(0.1)
+            else:
+                # Time's up.
+                raise Exception("Switch did not start before timeout")
+
+    def printBmv2Log(self):
+        if os.path.isfile(self.logfile):
+            print "-" * 80
+            print "%s log (from %s):" % (self.name, self.logfile)
+            with open(self.logfile, 'r') as f:
+                lines = f.readlines()
+                if len(lines) > BMV2_LOG_LINES:
+                    print "..."
+                for line in lines[-BMV2_LOG_LINES:]:
+                    print line.rstrip()
+
+    @staticmethod
+    def controllerIp(controllers):
+        try:
+            # onos.py
+            clist = controllers[0].nodes()
+        except AttributeError:
+            clist = controllers
+        assert len(clist) > 0
+        return random.choice(clist).IP()
+
+    def killBmv2(self, log=False):
+        if self.bmv2popen is not None:
+            self.bmv2popen.kill()
+        if self.logfd is not None:
+            if log:
+                self.logfd.write("*** PROCESS TERMINATED BY MININET ***\n")
+            self.logfd.close()
+
+    def cleanupTmpFiles(self):
+        self.cmd("rm -f /tmp/bmv2-%s-*" % self.name)
+
+    def stop(self, deleteIntfs=True):
+        """Terminate switch."""
+        self.stopped = True
+        self.killBmv2(log=True)
+        Switch.stop(self, deleteIntfs)
+
+
+# Exports for bin/mn
+switches = {'onosbmv2': ONOSBmv2Switch}
+hosts = {'onoshost': ONOSHost}
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/cfgtranslator.py b/TestON/tests/USECASE/SegmentRouting/dependencies/cfgtranslator.py
new file mode 100644
index 0000000..f38603e
--- /dev/null
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/cfgtranslator.py
@@ -0,0 +1,156 @@
+"""
+Copyright 2018 Open Networking Foundation ( ONF )
+
+Please refer questions to either the onos test mailing list at <onos-test@onosproject.org>,
+the System Testing Plans and Results wiki page at <https://wiki.onosproject.org/x/voMg>,
+or the System Testing Guide page at <https://wiki.onosproject.org/x/WYQg>
+
+    TestON is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 2 of the License, or
+    ( at your option ) any later version.
+
+    TestON is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with TestON.  If not, see <http://www.gnu.org/licenses/>.
+"""
+import json
+import re
+
+ONOS_GROUP_ID = 'org.onosproject'
+SR_APP = 'segmentrouting'
+DHCP_APP = 'dhcprelay'
+DHCP_APP_ID = ONOS_GROUP_ID + '.' + DHCP_APP
+
+# Translate configuration JSON file from BMv2 driver to OFDPA-OVS driver.
+def bmv2ToOfdpa( main, cfgFile="" ):
+    if not cfgFile:
+        cfgFile = "%s%s.json" % ( main.configPath + main.forJson,
+                                  main.cfgName )
+    with open( cfgFile ) as cfg:
+        netcfg = json.load( cfg )
+
+    if 'ports' in netcfg.keys():
+        for port in netcfg[ 'ports' ].keys():
+            searchObj = re.search( "device:bmv2:leaf([1-9][0-9]*)/([0-9]+)", port )
+            if searchObj:
+                new_port = 'of:' + searchObj.group( 1 ).zfill( 16 ) + '/' + searchObj.group( 2 )
+                netcfg[ 'ports' ][ new_port ] = netcfg[ 'ports' ].pop( port )
+
+    if 'hosts' in netcfg.keys():
+        for ( host, hostCfg ) in netcfg[ 'hosts' ].items():
+            if type( hostCfg[ 'basic' ][ 'locations' ] ) is list:
+                new_locations = []
+                for location in hostCfg[ 'basic' ][ 'locations' ]:
+                    searchObj = re.search( "device:bmv2:leaf([1-9][0-9]*)/([0-9]+)", location )
+                    if searchObj:
+                        new_locations.append( 'of:' + searchObj.group( 1 ).zfill( 16 ) + '/' + searchObj.group( 2 ) )
+                    else:
+                        new_locations.append( location )
+                netcfg[ 'hosts' ][ host ][ 'basic' ][ 'locations' ] = new_locations
+            else:
+                location = hostCfg[ 'basic' ][ 'locations' ]
+                searchObj = re.search( "device:bmv2:leaf([1-9][0-9]*)/([0-9]+)", location )
+                if searchObj:
+                    new_location = 'of:' + searchObj.group( 1 ).zfill( 16 ) + '/' + searchObj.group( 2 )
+                    netcfg[ 'hosts' ][ host ][ 'basic' ][ 'locations' ] = new_location
+
+    if 'devices' in netcfg.keys():
+        for device in netcfg[ 'devices' ].keys():
+            searchObj = re.search( "device:bmv2:(leaf|spine)([1-9][0-9]*)", device )
+            new_device = device
+            if searchObj:
+                new_device = 'of:' + searchObj.group( 2 ).zfill( 16 )
+                netcfg[ 'devices' ][ new_device ] = netcfg[ 'devices' ].pop( device )
+            if 'pairDeviceId' in netcfg[ 'devices' ][ new_device ][ SR_APP ].keys():
+                searchObj = re.search( "device:bmv2:leaf([1-9][0-9]*)",
+                                       netcfg[ 'devices' ][ new_device ][ SR_APP ][ 'pairDeviceId' ])
+                if searchObj:
+                    netcfg[ 'devices' ][ new_device ][ SR_APP ][ 'pairDeviceId' ] = 'of:' + \
+                                                                                    searchObj.group( 1 ).zfill( 16 )
+            if 'basic' in netcfg[ 'devices' ][ new_device ].keys():
+                netcfg[ 'devices' ][ new_device ][ 'basic' ].update( { 'driver': 'ofdpa-ovs' } )
+
+    if 'apps' in netcfg.keys():
+        if DHCP_APP_ID in netcfg[ 'apps' ].keys():
+            for i, dhcpcfg in enumerate( netcfg[ 'apps' ][ DHCP_APP_ID ][ 'default' ] ):
+                if 'dhcpServerConnectPoint' in dhcpcfg.keys():
+                    searchObj = re.search( "device:bmv2:leaf([1-9][0-9]*)/([0-9]+)",
+                                           dhcpcfg[ 'dhcpServerConnectPoint' ] )
+                    if searchObj:
+                        netcfg[ 'apps' ][ DHCP_APP_ID ][ 'default' ][ i ][ 'dhcpServerConnectPoint' ] = \
+                            'of:' + searchObj.group( 1 ).zfill(16) + '/' + searchObj.group( 2 )
+
+    with open( cfgFile, 'w' ) as cfg:
+        cfg.write( json.dumps( netcfg, indent=4, separators=( ',', ':' ) ) )
+
+# Translate configuration JSON file from OFDPA-OVS driver to BMv2 driver.
+def ofdpaToBmv2( main, cfgFile="" ):
+    if not cfgFile:
+        cfgFile = "%s%s.json" % ( main.configPath + main.forJson,
+                                  main.cfgName )
+    with open( cfgFile ) as cfg:
+        netcfg = json.load( cfg )
+
+    if 'ports' in netcfg.keys():
+        for port in netcfg[ 'ports' ].keys():
+            searchObj = re.search( "of:0*([1-9][0-9]*)/([0-9]+)", port )
+            if searchObj:
+                new_port = 'device:bmv2:leaf' + searchObj.group( 1 ) + '/' + searchObj.group( 2 )
+                netcfg[ 'ports' ][ new_port ] = netcfg[ 'ports' ].pop( port )
+
+    if 'hosts' in netcfg.keys():
+        for ( host, hostCfg ) in netcfg[ 'hosts' ].items():
+            if type( hostCfg[ 'basic' ][ 'locations' ] ) is list:
+                new_locations = []
+                for location in hostCfg[ 'basic' ][ 'locations' ]:
+                    searchObj = re.search( "of:0*([1-9][0-9]*)/([0-9]+)", location )
+                    if searchObj:
+                        new_locations.append( 'device:bmv2:leaf' + searchObj.group( 1 ) + '/' + searchObj.group( 2 ) )
+                    else:
+                        new_locations.append( location )
+                netcfg[ 'hosts' ][ host ][ 'basic' ][ 'locations' ] = new_locations
+            else:
+                location = hostCfg[ 'basic' ][ 'locations' ]
+                searchObj = re.search( "of:0*([1-9][0-9]*)/([0-9]+)", location )
+                if searchObj:
+                    new_location = 'device:bmv2:leaf' + searchObj.group( 1 ) + '/' + searchObj.group( 2 )
+                    netcfg[ 'hosts' ][ host ][ 'basic' ][ 'locations' ] = new_location
+
+    if 'devices' in netcfg.keys():
+        for device in netcfg[ 'devices' ].keys():
+            searchObj = re.search( "of:0*([1-9][0-9]*)", device )
+            new_device = device
+            if searchObj:
+                isLeaf = netcfg[ 'devices' ][ device ][ SR_APP ][ 'isEdgeRouter' ]
+                if isLeaf is True:
+                    new_device = 'device:bmv2:leaf' + searchObj.group( 1 )
+                else:
+                    new_device = 'device:bmv2:spine' + searchObj.group( 1 )
+                netcfg[ 'devices' ][ new_device ] = netcfg[ 'devices' ].pop( device )
+            if 'pairDeviceId' in netcfg[ 'devices' ][ new_device ][ SR_APP ].keys():
+                searchObj = re.search( "of:0*([1-9][0-9]*)",
+                                       netcfg[ 'devices' ][ new_device ][ SR_APP ][ 'pairDeviceId' ])
+                if searchObj:
+                    netcfg[ 'devices' ][ new_device ][ SR_APP ][ 'pairDeviceId' ] = 'device:bmv2:leaf' + \
+                                                                                    searchObj.group( 1 )
+            if 'basic' in netcfg[ 'devices' ][ new_device ].keys():
+                if 'driver' in netcfg[ 'devices' ][ new_device ][ 'basic' ].keys():
+                    del netcfg[ 'devices' ][ new_device ][ 'basic' ][ 'driver' ]
+
+    if 'apps' in netcfg.keys():
+        if DHCP_APP_ID in netcfg[ 'apps' ].keys():
+            for i, dhcpcfg in enumerate( netcfg[ 'apps' ][ DHCP_APP_ID ][ 'default' ] ):
+                if 'dhcpServerConnectPoint' in dhcpcfg.keys():
+                    searchObj = re.search( "of:0*([1-9][0-9]*)/([0-9]+)",
+                                           dhcpcfg[ 'dhcpServerConnectPoint' ] )
+                    if searchObj:
+                        netcfg[ 'apps' ][ DHCP_APP_ID ][ 'default' ][ i ][ 'dhcpServerConnectPoint' ] = \
+                            'device:bmv2:leaf' + searchObj.group( 1 ) + '/' + searchObj.group( 2 )
+
+    with open( cfgFile, 'w' ) as cfg:
+        cfg.write( json.dumps( netcfg, indent=4, separators=( ',', ':' ) ) )
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/hagg_fabric.py b/TestON/tests/USECASE/SegmentRouting/dependencies/hagg_fabric.py
index b4bb587..814b6fb 100644
--- a/TestON/tests/USECASE/SegmentRouting/dependencies/hagg_fabric.py
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/hagg_fabric.py
@@ -3,7 +3,7 @@
 import re
 from optparse import OptionParser
 from ipaddress import ip_network
-from mininet.node import RemoteController, OVSBridge, Host
+from mininet.node import RemoteController, OVSBridge, Host, OVSSwitch
 from mininet.link import TCLink
 from mininet.log import setLogLevel
 from mininet.net import Mininet
@@ -14,6 +14,8 @@
 from routinglib import BgpRouter, RoutedHost
 from trellislib import DhcpServer, TaggedRoutedHost, DualHomedRoutedHost, DualHomedTaggedRoutedHost, DhcpClient, Dhcp6Client, DhcpServer, Dhcp6Server, TrellisHost
 
+from bmv2 import ONOSBmv2Switch
+
 # Parse command line options and dump results
 def parseOptions():
     "Parse command line options"
@@ -27,13 +29,25 @@
     parser.add_option( '--ipv4', dest='ipv4', type='int', default=1,
                        help='Configure hosts with ipv4 or not' )
     parser.add_option( '--onos-ip', dest='onosIp', type='str', default='',
-                        help='IP address list of ONOS instances, separated by comma(,). Overrides --onos option' )
+                       help='IP address list of ONOS instances, separated by comma(,). Overrides --onos option' )
+    parser.add_option( '--switch', dest='switch', type='str', default='ovs',
+                       help='Switch type: ovs, bmv2 (with fabric.p4)' )
 
     ( options, args ) = parser.parse_args()
     return options, args
 
 opts, args = parseOptions()
 
+FABRIC_PIPECONF = "org.onosproject.pipelines.fabric"
+
+SWITCH_TO_PARAMS_DICT = {
+    "ovs": dict(cls=OVSSwitch),
+    "bmv2": dict(cls=ONOSBmv2Switch, pipeconf=FABRIC_PIPECONF)
+}
+if opts.switch not in SWITCH_TO_PARAMS_DICT:
+    raise Exception("Unknown switch type '%s'" % opts.switch)
+SWITCH_PARAMS = SWITCH_TO_PARAMS_DICT[opts.switch]
+
 class ComcastLeafSpineFabric(Topo):
 
     spines = dict()
@@ -237,11 +251,15 @@
 
         # Create spine switches
         for s in range(spine):
-            self.spines[s] = self.addSwitch('spine10%s' % (s + 1), dpid = "00000000010%s" % (s + 1) )
+            self.spines[s] = self.addSwitch( 'spine10%s' % (s + 1),
+                                             dpid = "00000000010%s" % (s + 1),
+                                             **SWITCH_PARAMS )
 
         # Create leaf switches
         for ls in range(leaf):
-            self.leafs[ls] = self.addSwitch('leaf%s' % (ls + 1), dpid = "00000000000%s" % ( ls + 1) )
+            self.leafs[ls] = self.addSwitch( 'leaf%s' % (ls + 1),
+                                             dpid = "00000000000%s" % (ls + 1),
+                                             **SWITCH_PARAMS )
 
         # connecting leaf and spines, leafs 1-5 have double links
         for s in range(2):
diff --git a/TestON/tests/USECASE/SegmentRouting/dependencies/trellis_fabric.py b/TestON/tests/USECASE/SegmentRouting/dependencies/trellis_fabric.py
index 73bfda3..e02a675 100644
--- a/TestON/tests/USECASE/SegmentRouting/dependencies/trellis_fabric.py
+++ b/TestON/tests/USECASE/SegmentRouting/dependencies/trellis_fabric.py
@@ -16,6 +16,8 @@
 from trellislib import TrellisHost, DhcpRelay
 from functools import partial
 
+from bmv2 import ONOSBmv2Switch
+
 # Parse command line options and dump results
 def parseOptions():
     "Parse command line options"
@@ -43,14 +45,24 @@
                        help='Use another DHCP server for indirectly connected DHCP clients if True' )
     parser.add_option( '--remote-dhcp-server', action="store_true", dest='remoteServer', default=False,
                        help='Connect DHCP server indirectly (via gateway) if True' )
+    parser.add_option( '--switch', dest='switch', type='str', default='ovs',
+                       help='Switch type: ovs, bmv2 (with fabric.p4)' )
     ( options, args ) = parser.parse_args()
     return options, args
 
-
 opts, args = parseOptions()
 
 IP6_SUBNET_CLASS = 120
 IP4_SUBNET_CLASS = 24
+FABRIC_PIPECONF = "org.onosproject.pipelines.fabric"
+
+SWITCH_TO_PARAMS_DICT = {
+    "ovs": dict(cls=OVSSwitch),
+    "bmv2": dict(cls=ONOSBmv2Switch, pipeconf=FABRIC_PIPECONF)
+}
+if opts.switch not in SWITCH_TO_PARAMS_DICT:
+    raise Exception("Unknown switch type '%s'" % opts.switch)
+SWITCH_PARAMS = SWITCH_TO_PARAMS_DICT[opts.switch]
 
 # TODO: DHCP support
 class IpHost( Host ):
@@ -111,11 +123,15 @@
         linkopts = dict( bw=100 )
         # Create spine switches
         for s in range(spine):
-            spines[s] = self.addSwitch('spine10%s' % (s + 1), dpid = "00000000010%s" % (s + 1) )
+            spines[s] = self.addSwitch( 'spine10%s' % (s + 1),
+                                        dpid="00000000010%s" % (s + 1),
+                                        **SWITCH_PARAMS )
 
         # Create leaf switches
         for ls in range(leaf):
-            leafs[ls] = self.addSwitch('leaf%s' % (ls + 1), dpid = "00000000000%s" % ( ls + 1) )
+            leafs[ls] = self.addSwitch( 'leaf%s' % (ls + 1),
+                                        dpid="00000000000%s" % (ls + 1),
+                                        **SWITCH_PARAMS )
 
             # Connect leaf to all spines with dual link
             for s in range( spine ):
@@ -239,11 +255,15 @@
 
         # Create spine switches
         for s in range(spine):
-            spines[s] = self.addSwitch('spine10%s' % (s + 1), dpid = "00000000010%s" % (s + 1) )
+            spines[s] = self.addSwitch( 'spine10%s' % (s + 1),
+                                        dpid="00000000010%s" % (s + 1),
+                                        **SWITCH_PARAMS )
 
         # Create leaf switches
         for ls in range(leaf):
-            leafs[ls] = self.addSwitch('leaf%s' % (ls + 1), dpid = "00000000000%s" % ( ls + 1) )
+            leafs[ls] = self.addSwitch( 'leaf%s' % (ls + 1),
+                                        dpid="00000000000%s" % (ls + 1),
+                                        **SWITCH_PARAMS )
 
             # Connect leaf to all spines
             for s in range( spine ):
diff --git a/TestON/tests/dependencies/Cluster.py b/TestON/tests/dependencies/Cluster.py
index 6d82489..7e850ba 100644
--- a/TestON/tests/dependencies/Cluster.py
+++ b/TestON/tests/dependencies/Cluster.py
@@ -516,7 +516,7 @@
                 if ips == activeIps:
                     currentResult = True
                 else:
-                    main.log.error( "{} != {}".format( ips, activeIps ) )
+                    main.log.warn( "{} != {}".format( ips, activeIps ) )
             except ( ValueError, TypeError ):
                 main.log.error( "Error parsing nodes output" )
                 main.log.warn( repr( i ) )