Update Cluster Driver

Change-Id: I8a3a57e19637ff210548e57d41178e6f194cf694
diff --git a/TestON/tests/SCPF/SCPFcbench/SCPFcbench.py b/TestON/tests/SCPF/SCPFcbench/SCPFcbench.py
index 68a94e5..d87bc7e 100644
--- a/TestON/tests/SCPF/SCPFcbench/SCPFcbench.py
+++ b/TestON/tests/SCPF/SCPFcbench/SCPFcbench.py
@@ -19,10 +19,10 @@
         import time
         import os
         global init
-        main.case("pre-condition for cbench test.")
+        main.case( "pre-condition for cbench test." )
 
         try:
-            if type(init) is not bool:
+            if type( init ) is not bool:
                 init = False
         except NameError:
             init = False
@@ -40,40 +40,40 @@
             stepResult = main.FALSE
             try:
                 # Load values from params file
-                BENCHIp = main.params['BENCH']['ip1']
-                BENCHUser = main.params['BENCH']['user']
-                CBENCHuser = main.params['CBENCH']['user']
-                MN1Ip = os.environ[main.params['MN']['ip1']]
-                main.maxNodes = int(main.params['availableNodes'])
-                main.cellName = main.params['ENV']['cellName']
-                main.apps = main.params['ENV']['cellApps']
-                main.scale = (main.params['SCALE']).split(",")
+                BENCHIp = main.params[ 'BENCH' ][ 'ip1' ]
+                BENCHUser = main.params[ 'BENCH' ][ 'user' ]
+                CBENCHuser = main.params[ 'CBENCH' ][ 'user' ]
+                MN1Ip = os.environ[ main.params[ 'MN' ][ 'ip1' ] ]
+                main.maxNodes = int( main.params[ 'availableNodes' ] )
+                main.cellName = main.params[ 'ENV' ][ 'cellName' ]
+                main.apps = main.params[ 'ENV' ][ 'cellApps' ]
+                main.scale = ( main.params[ 'SCALE' ] ).split( "," )
 
-                stepResult = main.testSetUp.envSetup( hasCli=False )
+                stepResult = main.testSetUp.envSetup()
             except Exception as e:
                 main.testSetUp.envSetupException( e )
             main.testSetUp.evnSetupConclusion( stepResult )
             main.commit = ( main.commit.split( " " ) )[ 1 ]
         # -- END OF INIT SECTION --#
 
-        main.testSetUp.ONOSSetUp( MN1Ip, True, hasCli=False,
+        main.testSetUp.ONOSSetUp( MN1Ip, main.Cluster, True,
                                   cellName=main.cellName )
 
-        for i in range(5):
-            main.ONOSbench.onosCfgSet(main.ONOSip[0], "org.onosproject.fwd.ReactiveForwarding","packetOutOnly true")
-            time.sleep(5)
-            main.ONOSbench.handle.sendline("onos $OC1 cfg get|grep packetOutOnly")
-            main.ONOSbench.handle.expect(":~")
+        for i in range( 5 ):
+            main.ONOSbench.onosCfgSet( main.Cluster.active( 0 ).ipAddress, "org.onosproject.fwd.ReactiveForwarding", "packetOutOnly true" )
+            time.sleep( 5 )
+            main.ONOSbench.handle.sendline( "onos $OC1 cfg get|grep packetOutOnly" )
+            main.ONOSbench.handle.expect( ":~" )
             check = main.ONOSbench.handle.before
             if "value=true" in check:
-                main.log.info("cfg set successful")
+                main.log.info( "cfg set successful" )
                 stepResult = main.TRUE
                 break
             if i == 4:
-                main.log.info("Cfg set failed")
+                main.log.info( "Cfg set failed" )
                 stepResult = main.FALSE
             else:
-                time.sleep(5)
+                time.sleep( 5 )
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
@@ -82,59 +82,59 @@
 
 
     def CASE2( self, main ):
-        main.case("Running Cbench")
-        main.step("Issuing cbench commands and grab returned results")
+        main.case( "Running Cbench" )
+        main.step( "Issuing cbench commands and grab returned results" )
         validFlag = False
         cbenchCMD = main.params[ 'TEST' ][ 'cbenchCMD' ]
         mode = main.params[ 'TEST' ][ 'mode' ]
         if mode != "t":
             mode = " "
 
-        runCbench = ( "ssh " + CBENCHuser + "@" + main.ONOSip[0] + " " + cbenchCMD + mode )
-        main.ONOSbench.handle.sendline(runCbench)
-        time.sleep(30)
-        main.ONOSbench.handle.expect(":~")
+        runCbench = ( "ssh " + CBENCHuser + "@" + main.Cluster.active( 0 ).ipAddress + " " + cbenchCMD + mode )
+        main.ONOSbench.handle.sendline( runCbench )
+        time.sleep( 30 )
+        main.ONOSbench.handle.expect( ":~" )
         output = main.ONOSbench.handle.before
-        main.log.info(output)
+        main.log.info( output )
 
         output = output.splitlines()
         for line in output:
             if "RESULT: " in line:
                 validFlag = True
                 print line
-                resultLine = line.split(" ")
+                resultLine = line.split( " " )
                 for word in resultLine:
                     if word == "min/max/avg/stdev":
-                        resultsIndex = resultLine.index(word)
+                        resultsIndex = resultLine.index( word )
                         print resultsIndex
                         break
 
-                finalDataString = resultLine[resultsIndex + 2]
+                finalDataString = resultLine[ resultsIndex + 2 ]
                 print finalDataString
-                finalDataList = finalDataString.split("/")
-                avg = finalDataList[2]
-                stdev = finalDataList[3]
+                finalDataList = finalDataString.split( "/" )
+                avg = finalDataList[ 2 ]
+                stdev = finalDataList[ 3 ]
 
-                main.log.info("Average: \t\t\t" + avg)
-                main.log.info("Standard Deviation: \t" + stdev)
+                main.log.info( "Average: \t\t\t" + avg )
+                main.log.info( "Standard Deviation: \t" + stdev )
 
                 try:
                     dbFileName="/tmp/CbenchDB"
-                    dbfile = open(dbFileName, "w+")
+                    dbfile = open( dbFileName, "w+" )
                     temp = "'" + main.commit + "',"
                     temp += "'" + mode + "',"
                     temp += "'" + avg + "',"
                     temp += "'" + stdev + "'\n"
-                    dbfile.write(temp)
+                    dbfile.write( temp )
                     dbfile.close()
-                    main.ONOSbench.logReport(main.ONOSip[0], ["ERROR", "WARNING", "EXCEPT"], outputMode="d")
+                    main.ONOSbench.logReport( main.Cluster.active( 0 ).ipAddress, [ "ERROR", "WARNING", "EXCEPT" ], outputMode="d" )
                 except IOError:
-                    main.log.warn("Error opening " + dbFileName + " to write results.")
+                    main.log.warn( "Error opening " + dbFileName + " to write results." )
 
                 stepResult = main.TRUE
                 break
         if ( validFlag == False ):
-            main.log.warn("Cbench Test produced no valid results!!!!")
+            main.log.warn( "Cbench Test produced no valid results!!!!" )
             stepResult = main.FALSE
 
         utilities.assert_equals( expect=main.TRUE,
diff --git a/TestON/tests/SCPF/SCPFcbench/SCPFcbench.topo b/TestON/tests/SCPF/SCPFcbench/SCPFcbench.topo
index ebd5fd4..ff9a459 100644
--- a/TestON/tests/SCPF/SCPFcbench/SCPFcbench.topo
+++ b/TestON/tests/SCPF/SCPFcbench/SCPFcbench.topo
@@ -2,29 +2,26 @@
 
     <COMPONENT>
 
-        <ONOSbench>
-            <host>localhost</host>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
             <user>sdn</user>
             <password>rocks</password>
-            <type>OnosDriver</type>
+            <type>OnosClusterDriver</type>
             <connect_order>1</connect_order>
             <COMPONENTS>
-                <home>~/onos</home>
-                <nodes>1</nodes>
-                <prompt></prompt>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost></diff_clihost>  # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
+                <karaf_username></karaf_username>
+                <karaf_password></karaf_password>
+                <web_user></web_user>
+                <web_pass></web_pass>
+                <rest_port></rest_port>
+                <prompt></prompt>  # TODO: we technically need a few of these, one per component
+                <onos_home></onos_home>  # defines where onos home is
+                <nodes> 1 </nodes>  # number of nodes in the cluster
             </COMPONENTS>
-        </ONOSbench>
+        </ONOScell>
 
-        <ONOScli1>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>9</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli1>
     </COMPONENT>
 
 </TOPOLOGY>
diff --git a/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.params b/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.params
index a263ca6..99d17ba 100644
--- a/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.params
+++ b/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.params
@@ -24,7 +24,10 @@
         <testCMD1> -n </testCMD1>                       #neighbors
 
     </TEST>
-
+    <CFG>
+        <flowRule>org.onosproject.store.flow.impl.DistributedFlowRuleStore</flowRule>
+        <nullProvider>org.onosproject.provider.nil.NullProviders</nullProvider>
+    </CFG>
     <GIT>
         <pull>False</pull>
         <branch>master</branch>
diff --git a/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.py b/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.py
index 1de518f..f6325ad 100644
--- a/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.py
+++ b/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.py
@@ -63,19 +63,21 @@
                 BENCHUser = main.params[ 'BENCH' ][ 'user' ]
                 BENCHIp = main.params[ 'BENCH' ][ 'ip1' ]
                 main.scale = ( main.params[ 'SCALE' ]  ).split( "," )
+                main.flowRuleCfg = main.params[ 'CFG' ][ 'flowRule' ]
+                main.nullProviderCfg = main.params[ 'CFG' ][ 'nullProvider' ]
                 stepResult = main.testSetUp.envSetup()
                 resultsDB = open( "/tmp/flowTP1gDB", "w+" )
                 resultsDB.close()
             except Exception as e:
                 main.testSetUp.envSetupException( e )
             main.testSetUp.evnSetupConclusion( stepResult )
-            main.commit = (main.commit.split(" "))[1]
+            main.commit = ( main.commit.split( " " ) )[ 1 ]
         # -- END OF INIT SECTION --#
 
-        main.testSetUp.ONOSSetUp( "localhost", True, cellName=cellName )
+        main.testSetUp.ONOSSetUp( "localhost", main.Cluster, True, cellName=cellName )
 
-        main.log.info("Startup sequence complete")
-        main.ONOSbench.logReport(main.ONOSip[0], ["ERROR", "WARNING", "EXCEPT"], outputMode="d")
+        main.log.info( "Startup sequence complete" )
+        main.ONOSbench.logReport( main.Cluster.active( 0 ).ipAddress, [ "ERROR", "WARNING", "EXCEPT" ], outputMode="d" )
 
     def CASE2( self, main ):
         #
@@ -92,230 +94,238 @@
         try:
             currentNeighbors
         except:
-            currentNeighbors = (main.params[ 'TEST' ][ 'neighbors' ]).split(",")[0]
+            currentNeighbors = ( main.params[ 'TEST' ][ 'neighbors' ] ).split( "," )[ 0 ]
         else:
             if currentNeighbors == "r":      #reset
                 currentNeighbors = "0"
             else:
                 currentNeighbors = "a"
 
-        testCMD = [ 0,0,0,0 ]
-        warmUp = int(main.params[ 'TEST' ][ 'warmUp' ])
-        sampleSize = int(main.params[ 'TEST' ][ 'sampleSize' ])
-        switches = int(main.params[ 'TEST' ][ 'switches' ])
-        neighborList = (main.params[ 'TEST' ][ 'neighbors' ]).split(",")
-        testCMD[0] = main.params[ 'TEST' ][ 'testCMD0' ]
-        testCMD[1] = main.params[ 'TEST' ][ 'testCMD1' ]
+        testCMD = [ 0, 0, 0, 0 ]
+        warmUp = int( main.params[ 'TEST' ][ 'warmUp' ] )
+        sampleSize = int( main.params[ 'TEST' ][ 'sampleSize' ] )
+        switches = int( main.params[ 'TEST' ][ 'switches' ] )
+        neighborList = ( main.params[ 'TEST' ][ 'neighbors' ] ).split( "," )
+        testCMD[ 0 ] = main.params[ 'TEST' ][ 'testCMD0' ]
+        testCMD[ 1 ] = main.params[ 'TEST' ][ 'testCMD1' ]
         cooldown = main.params[ 'TEST' ][ 'cooldown' ]
         cellName = main.params[ 'ENV' ][ 'cellName' ]
         BENCHIp = main.params[ 'BENCH' ][ 'ip1' ]
         BENCHUser = main.params[ 'BENCH' ][ 'user' ]
         MN1Ip = main.params[ 'MN' ][ 'ip1' ]
-        homeDir = os.path.expanduser('~')
-        flowRuleBackup = str(main.params[ 'TEST' ][ 'enableFlowRuleStoreBackup' ])
-        main.log.info("Flow Rule Backup is set to:" + flowRuleBackup)
+        homeDir = os.path.expanduser( '~' )
+        flowRuleBackup = str( main.params[ 'TEST' ][ 'enableFlowRuleStoreBackup' ] )
+        main.log.info( "Flow Rule Backup is set to:" + flowRuleBackup )
 
-        servers = str( main.numCtrls )
+        servers = str( main.Cluster.numCtrls )
 
-        if main.numCtrls == 1:
-            neighborList = ['0']
+        if main.Cluster.numCtrls == 1:
+            neighborList = [ '0' ]
             currentNeighbors = "r"
         else:
             if currentNeighbors == "a":
-                neighborList = [ str( main.numCtrls - 1 ) ]
+                neighborList = [ str( main.Cluster.numCtrls - 1 ) ]
                 currentNeighbors = "r"
             else:
-                neighborList = ['0']
+                neighborList = [ '0' ]
 
-        main.log.info("neightborlist: " + str(neighborList))
+        main.log.info( "neightborlist: " + str( neighborList ) )
 
         ts = time.time()
-        st = datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S')
+        st = datetime.datetime.fromtimestamp( ts ).strftime( '%Y-%m-%d %H:%M:%S' )
 
         for n in neighborList:
-            main.step("\tSTARTING TEST")
-            main.step("\tLOADING FROM SERVERS:  \t" + str( main.numCtrls ) )
-            main.step("\tNEIGHBORS:\t" + n )
-            main.log.info("=============================================================")
-            main.log.info("=============================================================")
+            main.step( "\tSTARTING TEST" )
+            main.step( "\tLOADING FROM SERVERS:  \t" + str( main.Cluster.numCtrls ) )
+            main.step( "\tNEIGHBORS:\t" + n )
+            main.log.info( "=============================================================" )
+            main.log.info( "=============================================================" )
             #write file to configure nil link
             ipCSV = ""
-            for i in range ( main.maxNodes ):
+            for i in range ( main.Cluster.maxCtrls ):
                 tempstr = "ip" + str( i + 1 )
                 ipCSV += main.params[ 'CTRL' ][ tempstr ]
-                if i + 1 < main.maxNodes:
+                if i + 1 < main.Cluster.maxCtrls:
                     ipCSV +=","
 
-            main.ONOSbench.onosCfgSet(main.ONOSip[0], "org.onosproject.store.flow.impl.DistributedFlowRuleStore", "backupCount 1")
-            for i in range(3):
-                main.ONOSbench.onosCfgSet(main.ONOSip[0], "org.onosproject.provider.nil.NullProviders", "deviceCount 35")
-                main.ONOSbench.onosCfgSet(main.ONOSip[0], "org.onosproject.provider.nil.NullProviders", "topoShape linear")
-                main.ONOSbench.onosCfgSet(main.ONOSip[0], "org.onosproject.provider.nil.NullProviders", "enabled true")
+            main.ONOSbench.onosCfgSet( main.Cluster.active( 0 ).ipAddress,
+                                       main.flowRuleCfg,
+                                       "backupCount 1" )
+            for i in range( 3 ):
+                main.ONOSbench.onosCfgSet( main.Cluster.active( 0 ).ipAddress,
+                                           main.nullProviderCfg,
+                                           "deviceCount 35" )
+                main.ONOSbench.onosCfgSet( main.Cluster.active( 0 ).ipAddress,
+                                           main.nullProviderCfg,
+                                           "topoShape linear" )
+                main.ONOSbench.onosCfgSet( main.Cluster.active( 0 ).ipAddress,
+                                           main.nullProviderCfg,
+                                           "enabled true" )
 
-                time.sleep(5)
-                main.ONOSbench.handle.sendline("onos $OC1 summary")
-                main.ONOSbench.handle.expect(":~")
+                time.sleep( 5 )
+                main.ONOSbench.handle.sendline( "onos $OC1 summary" )
+                main.ONOSbench.handle.expect( ":~" )
                 check = main.ONOSbench.handle.before
-                main.log.info("\nStart up check: \n" + check + "\n")
+                main.log.info( "\nStart up check: \n" + check + "\n" )
                 if "SCC(s)=1," in check:
                     main.ONOSbench.handle.sendline( "onos $OC1 balance-masters" )
                     main.ONOSbench.handle.expect( ":~" )
-                    time.sleep(5)
-                    main.ONOSbench.handle.sendline( "onos $OC1 roles ")
+                    time.sleep( 5 )
+                    main.ONOSbench.handle.sendline( "onos $OC1 roles " )
                     main.ONOSbench.handle.expect ( ":~" )
                     main.log.info( "switch masterships:" + str( main.ONOSbench.handle.before ) )
                     break
-                time.sleep(5)
+                time.sleep( 5 )
 
             #devide flows
-            flows = int(main.params[ 'TEST' ][ 'flows' ])
-            main.log.info("Flow Target  = " + str(flows))
+            flows = int( main.params[ 'TEST' ][ 'flows' ] )
+            main.log.info( "Flow Target  = " + str( flows ) )
 
-            flows = (flows *max(int(n)+1,int(servers)))/((int(n) + 1)*int(servers)*(switches))
+            flows = ( flows *max( int( n )+1, int( servers ) ) )/( ( int( n ) + 1 ) * int( servers )*( switches ) )
 
-            main.log.info("Flows per switch = " + str(flows))
+            main.log.info( "Flows per switch = " + str( flows ) )
 
             #build list of servers in "$OC1, $OC2...." format
             serverEnvVars = ""
             for i in range( int( servers ) ):
-                serverEnvVars += ( "-s " + main.ONOSip[ i ] + " " )
+                serverEnvVars += ( "-s " + main.Cluster.active( i ).ipAddress + " " )
 
-            data = [[""]*int(servers)]*int(sampleSize)
-            maxes = [""]*int(sampleSize)
+            data = [ [ "" ]*int( servers ) ]*int( sampleSize )
+            maxes = [ "" ]*int( sampleSize )
 
             flowCMD = "python3 " + homeDir + "/onos/tools/test/bin/"
-            flowCMD += testCMD[0] + " " + str(flows) + " " + testCMD[1]
-            flowCMD += " " + str(n) + " " + str(serverEnvVars) + "-j"
+            flowCMD += testCMD[ 0 ] + " " + str( flows ) + " " + testCMD[ 1 ]
+            flowCMD += " " + str( n ) + " " + str( serverEnvVars ) + "-j"
 
-            main.log.info(flowCMD)
-            #time.sleep(60)
+            main.log.info( flowCMD )
+            #time.sleep( 60 )
 
-            for test in range(0, warmUp + sampleSize):
+            for test in range( 0, warmUp + sampleSize ):
                 if test < warmUp:
-                    main.log.info("Warm up " + str(test + 1) + " of " + str(warmUp))
+                    main.log.info( "Warm up " + str( test + 1 ) + " of " + str( warmUp ) )
                 else:
-                     main.log.info("====== Test run: " + str(test-warmUp+1) + " ======")
+                     main.log.info( "====== Test run: " + str( test-warmUp+1 ) + " ======" )
 
-                main.ONOSbench.handle.sendline(flowCMD)
-                main.ONOSbench.handle.expect(":~")
+                main.ONOSbench.handle.sendline( flowCMD )
+                main.ONOSbench.handle.expect( ":~" )
                 rawResult = main.ONOSbench.handle.before
-                main.log.info("Raw results: \n" + rawResult + "\n")
+                main.log.info( "Raw results: \n" + rawResult + "\n" )
 
                 if "failed" in rawResult:
-                    main.log.report("FLOW_TESTER.PY FAILURE")
-                    main.log.report( " \n" + rawResult + " \n")
-                    for i in range( main.numCtrls ):
-                        main.log.report("=======================================================")
-                        main.log.report(" ONOS " + str( i + 1 ) + "LOG REPORT")
-                        main.ONOSbench.logReport( main.ONOSip[ i ], ["ERROR", "WARNING", "EXCEPT"], outputMode="d" )
-                    main.ONOSbench.handle.sendline("onos $OC1 flows")
-                    main.ONOSbench.handle.expect(":~")
-                    main.log.info(main.ONOSbench.handle.before)
+                    main.log.report( "FLOW_TESTER.PY FAILURE" )
+                    main.log.report( " \n" + rawResult + " \n" )
+                    for ctrl in main.Cluster.active():
+                        main.log.report( "=======================================================" )
+                        main.log.report( ctrl.name + "LOG REPORT" )
+                        main.ONOSbench.logReport( ctrl.ipAddress, [ "ERROR", "WARNING", "EXCEPT" ], outputMode="d" )
+                    main.ONOSbench.handle.sendline( "onos $OC1 flows" )
+                    main.ONOSbench.handle.expect( ":~" )
+                    main.log.info( main.ONOSbench.handle.before )
 
                     break
 
             ########################################################################################
-                result = [""]*( main.numCtrls )
+                result = [ "" ]*( main.Cluster.numCtrls )
 
-                #print("rawResult: " + rawResult)
+                #print( "rawResult: " + rawResult )
 
                 rawResult = rawResult.splitlines()
 
-                for node in range( main.numCtrls ):
+                for node in range( main.Cluster.numCtrls ):
                     for line in rawResult:
-                        #print("line: " + line)
-                        if main.ONOSip[ node ] in line and "server" in line:
+                        #print( "line: " + line )
+                        if main.Cluster.active( node ).ipAddress in line and "server" in line:
                             temp = line.split( " " )
                             for word in temp:
-                                #print ("word: " + word)
-                                if "elapsed" in repr(word):
-                                    index = temp.index(word) + 1
-                                    myParsed = (temp[index]).replace(",","")
-                                    myParsed = myParsed.replace("}","")
-                                    myParsed = int(myParsed)
+                                #print ( "word: " + word )
+                                if "elapsed" in repr( word ):
+                                    index = temp.index( word ) + 1
+                                    myParsed = ( temp[ index ] ).replace( ",", "" )
+                                    myParsed = myParsed.replace( "}", "" )
+                                    myParsed = int( myParsed )
                                     result[ node ] = myParsed
-                                    main.log.info( main.ONOSip[ node ] + " : " + str( myParsed ) )
+                                    main.log.info( main.Cluster.active( node ).ipAddress + " : " + str( myParsed ) )
                                     break
 
                 if test >= warmUp:
                     for i in result:
                         if i == "":
-                            main.log.error("Missing data point, critical failure incoming")
+                            main.log.error( "Missing data point, critical failure incoming" )
 
                     print result
-                    maxes[test-warmUp] = max(result)
-                    main.log.info("Data collection iteration: " + str(test-warmUp) + " of " + str(sampleSize))
-                    main.log.info("Throughput time: " + str(maxes[test-warmUp]) + "(ms)")
+                    maxes[ test-warmUp ] = max( result )
+                    main.log.info( "Data collection iteration: " + str( test-warmUp ) + " of " + str( sampleSize ) )
+                    main.log.info( "Throughput time: " + str( maxes[ test-warmUp ] ) + "(ms)" )
 
-                    data[test-warmUp] = result
+                    data[ test-warmUp ] = result
 
                 # wait for flows = 0
-                for checkCount in range(0,5):
-                    time.sleep(10)
-                    main.ONOSbench.handle.sendline("onos $OC1 summary")
-                    main.ONOSbench.handle.expect(":~")
+                for checkCount in range( 0, 5 ):
+                    time.sleep( 10 )
+                    main.ONOSbench.handle.sendline( "onos $OC1 summary" )
+                    main.ONOSbench.handle.expect( ":~" )
                     flowCheck = main.ONOSbench.handle.before
                     if "flows=0," in flowCheck:
-                        main.log.info("Flows removed")
+                        main.log.info( "Flows removed" )
                         break
                     else:
                         for line in flowCheck.splitlines():
                             if "flows=" in line:
-                                main.log.info("Current Summary: " + line)
+                                main.log.info( "Current Summary: " + line )
                     if checkCount == 2:
-                        main.log.info("Flows are stuck, moving on ")
+                        main.log.info( "Flows are stuck, moving on " )
 
 
-                time.sleep(5)
+                time.sleep( 5 )
 
-            main.log.info("raw data: " + str(data))
-            main.log.info("maxes:" + str(maxes))
+            main.log.info( "raw data: " + str( data ) )
+            main.log.info( "maxes:" + str( maxes ) )
 
 
             # report data
-            print("")
-            main.log.info("\t Results (measurments are in milliseconds)")
-            print("")
+            print( "" )
+            main.log.info( "\t Results (measurments are in milliseconds)" )
+            print( "" )
 
             nodeString = ""
-            for i in range(1, int(servers) + 1):
-                nodeString += ("\tNode " + str(i))
+            for i in range( 1, int( servers ) + 1 ):
+                nodeString += ( "\tNode " + str( i ) )
 
-            for test in range(0, sampleSize ):
-                main.log.info("\t Test iteration " + str(test + 1) )
-                main.log.info("\t------------------")
-                main.log.info(nodeString)
+            for test in range( 0, sampleSize ):
+                main.log.info( "\t Test iteration " + str( test + 1 ) )
+                main.log.info( "\t------------------" )
+                main.log.info( nodeString )
                 resultString = ""
 
-                for i in range(0, int(servers) ):
-                    resultString += ("\t" + str(data[test][i]) )
-                main.log.info(resultString)
+                for i in range( 0, int( servers ) ):
+                    resultString += ( "\t" + str( data[ test ][ i ] ) )
+                main.log.info( resultString )
 
-                print("\n")
+                print( "\n" )
 
-            avgOfMaxes = numpy.mean(maxes)
-            main.log.info("Average of max value from each test iteration: " + str(avgOfMaxes))
+            avgOfMaxes = numpy.mean( maxes )
+            main.log.info( "Average of max value from each test iteration: " + str( avgOfMaxes ) )
 
-            stdOfMaxes = numpy.std(maxes)
-            main.log.info("Standard Deviation of max values: " + str(stdOfMaxes))
-            print("\n\n")
+            stdOfMaxes = numpy.std( maxes )
+            main.log.info( "Standard Deviation of max values: " + str( stdOfMaxes ) )
+            print( "\n\n" )
 
-            avgTP = int(main.params[ 'TEST' ][ 'flows' ])  / avgOfMaxes #result in kflows/second
+            avgTP = int( main.params[ 'TEST' ][ 'flows' ] )  / avgOfMaxes #result in kflows/second
 
             tp = []
             for i in maxes:
-                tp.append((int(main.params[ 'TEST' ][ 'flows' ]) / i ))
+                tp.append( ( int( main.params[ 'TEST' ][ 'flows' ] ) / i ) )
 
-            stdTP = numpy.std(tp)
+            stdTP = numpy.std( tp )
 
-            main.log.info("Average thoughput:  " + str(avgTP) + " Kflows/second" )
-            main.log.info("Standard deviation of throughput: " + str(stdTP) + " Kflows/second")
+            main.log.info( "Average thoughput:  " + str( avgTP ) + " Kflows/second" )
+            main.log.info( "Standard deviation of throughput: " + str( stdTP ) + " Kflows/second" )
 
             resultsLog = open( "/tmp/flowTP1gDB", "a" )
             resultString = ( "'" + main.commit + "'," )
             resultString += ( "'1gig'," )
-            resultString += ( (main.params[ 'TEST' ][ 'flows' ] ) + "," )
-            resultString += ( str( main.numCtrls ) + "," )
+            resultString += ( ( main.params[ 'TEST' ][ 'flows' ] ) + "," )
+            resultString += ( str( main.Cluster.numCtrls ) + "," )
             resultString += ( str( n ) + "," )
             resultString += ( str( avgTP ) + "," + str( stdTP ) + "\n" )
             resultsLog.write( resultString )
@@ -323,4 +333,4 @@
 
             main.log.report( "Result line to file: " + resultString )
 
-        main.ONOSbench.logReport( main.ONOSip[ 0 ], [ "ERROR", "WARNING", "EXCEPT" ], outputMode="d" )
+        main.ONOSbench.logReport( main.Cluster.active( 0 ).ipAddress, [ "ERROR", "WARNING", "EXCEPT" ], outputMode="d" )
diff --git a/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.topo b/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.topo
index 2e274da..93e1e9d 100644
--- a/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.topo
+++ b/TestON/tests/SCPF/SCPFflowTp1g/SCPFflowTp1g.topo
@@ -1,177 +1,25 @@
 <TOPOLOGY>
-
     <COMPONENT>
 
-        <ONOSbench>
-            <host>localhost</host>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
             <user>sdn</user>
             <password>rocks</password>
-            <type>OnosDriver</type>
+            <type>OnosClusterDriver</type>
             <connect_order>1</connect_order>
             <COMPONENTS>
-                <home>~/onos</home>
-                <nodes>7</nodes> 
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOSbench>
-
-        <ONOScli1>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>2</connect_order>
-            <COMPONENTS>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost></diff_clihost>  # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
                 <karaf_username></karaf_username>
                 <karaf_password></karaf_password>
-                <prompt></prompt>
+                <web_user></web_user>
+                <web_pass></web_pass>
+                <rest_port></rest_port>
+                <prompt></prompt>  # TODO: we technically need a few of these, one per component
+                <onos_home></onos_home>  # defines where onos home is
+                <nodes> 7 </nodes>  # number of nodes in the cluster
             </COMPONENTS>
-        </ONOScli1>
-
-        <ONOScli2>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>3</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli2>
-
-        <ONOScli3>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>4</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli3>
-
-        <ONOScli4>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>5</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli4>
-
-        <ONOScli5>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>6</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli5>
-
-        <ONOScli6>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>7</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli6>
-
-        <ONOScli7>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>8</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli7>
-
-        <ONOS1>
-            <host>OC1</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>9</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS1>
-
-        <ONOS2>
-            <host>OC2</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>10</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS2>
-
-        <ONOS3>
-            <host>OC3</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>11</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS3>
-
-        <ONOS4>
-            <host>OC4</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>12</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS4>
-    
-        <ONOS5>
-            <host>OC5</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>13</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS5>
-
-        <ONOS6>
-            <host>OC6</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>14</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS6>
-
-        <ONOS7>
-            <host>OC7</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>15</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS7>
+        </ONOScell>
 
     </COMPONENT>
-
-</TOPOLOGY>
- 
+</TOPOLOGY>
\ No newline at end of file
diff --git a/TestON/tests/SCPF/SCPFhostLat/SCPFhostLat.params b/TestON/tests/SCPF/SCPFhostLat/SCPFhostLat.params
index be4caec..566bd34 100644
--- a/TestON/tests/SCPF/SCPFhostLat/SCPFhostLat.params
+++ b/TestON/tests/SCPF/SCPFhostLat/SCPFhostLat.params
@@ -1,5 +1,5 @@
 <PARAMS>
-    <testcases>0,1,2,11,20,1,2,11,20,1,2,11,20,1,2,11,20</testcases>
+    <testcases>0,2,11,20,2,11,20,2,11,20,2,11,20</testcases>
 
     <SCALE>1,3,5,7</SCALE>
 
diff --git a/TestON/tests/SCPF/SCPFhostLat/SCPFhostLat.py b/TestON/tests/SCPF/SCPFhostLat/SCPFhostLat.py
index ec12f35..fb6dec8 100644
--- a/TestON/tests/SCPF/SCPFhostLat/SCPFhostLat.py
+++ b/TestON/tests/SCPF/SCPFhostLat/SCPFhostLat.py
@@ -33,7 +33,7 @@
     def __init__( self ):
         self.default = ''
 
-    def CASE0( self, main):
+    def CASE0( self, main ):
         import sys
         import json
         import time
@@ -83,18 +83,13 @@
             main.log.info( "Cresting DB file" )
             with open( main.dbFileName, "w+" ) as dbFile:
                 dbFile.write( "" )
-            stepResult = main.testSetUp.gitPulling()
+            stepResult = main.testSetUp.envSetup()
         except Exception as e:
             main.testSetUp.envSetupException( e )
         main.testSetUp.evnSetupConclusion( stepResult )
 
         main.commit = main.commit.split( " " )[ 1 ]
 
-    def CASE1( self ):
-        # main.scale[ 0 ] determines the current number of ONOS controller
-        main.testSetUp.getNumCtrls( True )
-        main.testSetUp.envSetup( includeGitPull=False, makeMaxNodes=False )
-
     def CASE2( self, main ):
         """
         - Uninstall ONOS cluster
@@ -102,17 +97,16 @@
         - Install ONOS cluster
         - Connect to cli
         """
-        main.testSetUp.ONOSSetUp( main.Mininet1, True,
-                                  cellName=main.cellName, killRemoveMax=False,
-                                  CtrlsSet=False )
+        main.testSetUp.ONOSSetUp( main.Mininet1, main.Cluster, True,
+                                  cellName=main.cellName, killRemoveMax=False )
 
     def CASE11( self, main ):
         main.log.info( "set and configure Application" )
         import json
         import time
-        time.sleep(main.startUpSleep)
+        time.sleep( main.startUpSleep )
         main.step( "Activating org.onosproject.proxyarp" )
-        appStatus = utilities.retry( main.ONOSrest1.activateApp,
+        appStatus = utilities.retry( main.Cluster.active( 0 ).REST.activateApp,
                                      main.FALSE,
                                      [ 'org.onosproject.proxyarp' ],
                                      sleep=3,
@@ -120,40 +114,41 @@
         utilities.assert_equals( expect=main.TRUE,
                                  actual=appStatus,
                                  onpass="Successfully activated proxyarp",
-                                 onfail="Failed to activated proxyarp")
+                                 onfail="Failed to activated proxyarp" )
 
         main.step( "Set up Default Topology Provider" )
         appStatus = main.TRUE
         configName = 'org.onosproject.net.topology.impl.DefaultTopologyProvider'
         configParam = 'maxEvents'
-        appStatus = appStatus and main.CLIs[0].setCfg(  configName, configParam,'1' )
+        appStatus = appStatus and main.Cluster.active( 0 ).CLI.setCfg( configName, configParam, '1' )
         configParam = 'maxBatchMs'
-        appStatus = appStatus and main.CLIs[0].setCfg( configName, configParam, '0')
+        appStatus = appStatus and main.Cluster.active( 0 ).CLI.setCfg( configName, configParam, '0' )
         configParam = 'maxIdleMs'
-        appStatus = appStatus and main.CLIs[0].setCfg( configName, configParam,'0' )
+        appStatus = appStatus and main.Cluster.active( 0 ).CLI.setCfg( configName, configParam, '0' )
         utilities.assert_equals( expect=main.TRUE,
                                  actual=appStatus,
                                  onpass="Successfully set DefaultTopologyProvider",
                                  onfail="Failed to set DefaultTopologyProvider" )
 
-        time.sleep( main.startUpSleep)
-        main.step('Starting mininet topology')
-        mnStatus = main.Mininet1.startNet(args='--topo=linear,1')
+        time.sleep( main.startUpSleep )
+        main.step( 'Starting mininet topology' )
+        mnStatus = main.Mininet1.startNet( args='--topo=linear,1' )
         utilities.assert_equals( expect=main.TRUE,
                                  actual=mnStatus,
                                  onpass="Successfully started Mininet",
                                  onfail="Failed to activate Mininet" )
-        main.step("Assinging masters to switches")
+        main.step( "Assinging masters to switches" )
         switches = main.Mininet1.getSwitches()
-        swStatus = main.Mininet1.assignSwController( sw=switches.keys(), ip=main.ONOSip[0] )
+        swStatus = main.Mininet1.assignSwController( sw=switches.keys(),
+                                                     ip=main.Cluster.active( 0 ).ipAddress )
         utilities.assert_equals( expect=main.TRUE,
                                  actual=swStatus,
                                  onpass="Successfully assigned switches to masters",
                                  onfail="Failed assign switches to masters" )
 
-        time.sleep( main.startUpSleep)
+        time.sleep( main.startUpSleep )
 
-    def CASE20(self, main):
+    def CASE20( self, main ):
         """
         host1 send arping package and measure latency
 
@@ -180,9 +175,9 @@
         # Host adding measurement
         assertion = main.TRUE
 
-        main.log.report('Latency of adding one host to ONOS')
-        main.log.report('First ' + str( main.iterIgnore ) + ' iterations ignored' + ' for jvm warmup time')
-        main.log.report('Total iterations of test: ' + str( main.numlter ) )
+        main.log.report( 'Latency of adding one host to ONOS' )
+        main.log.report( 'First ' + str( main.iterIgnore ) + ' iterations ignored' + ' for jvm warmup time' )
+        main.log.report( 'Total iterations of test: ' + str( main.numlter ) )
 
         addingHostTime = []
         metricsResultList = []
@@ -191,16 +186,16 @@
             with open( main.tsharkResultPath, "w" ) as dbFile:
                 dbFile.write( "" )
 
-            main.log.info('Starting tshark capture')
+            main.log.info( 'Starting tshark capture' )
             main.ONOSbench.tsharkGrep( main.tsharkPacketIn, main.tsharkResultPath )
             time.sleep( main.measurementSleep )
 
-            main.log.info('host 1 arping...')
-            main.Mininet1.arping(srcHost='h1', dstHost='10.0.0.2')
+            main.log.info( 'host 1 arping...' )
+            main.Mininet1.arping( srcHost='h1', dstHost='10.0.0.2' )
 
             time.sleep( main.measurementSleep )
 
-            main.log.info('Stopping all Tshark processes')
+            main.log.info( 'Stopping all Tshark processes' )
             main.ONOSbench.tsharkStop()
 
             time.sleep( main.measurementSleep )
@@ -209,7 +204,7 @@
             with open( main.tsharkResultPath, "r" ) as resultFile:
                 resultText = resultFile.readline()
                 main.log.info( 'Capture result:' + resultText )
-                resultText = resultText.split(' ')
+                resultText = resultText.split( ' ' )
                 if len( resultText ) > 1:
                     tsharkResultTime = float( resultText[ 1 ] ) * 1000.0
                 else:
@@ -220,8 +215,8 @@
             # Compare the timestemps, and get the lowest one.
             temp = 0;
             # Get host event timestamps from each nodes
-            for node in range ( 0, main.numCtrls ):
-                metricsResult = json.loads( main.CLIs[ node ].topologyEventsMetrics() )
+            for ctrl in main.Cluster.active():
+                metricsResult = json.loads( ctrl.CLI.topologyEventsMetrics() )
                 metricsResult = metricsResult.get( main.hostTimestampKey ).get( "value" )
                 main.log.info( "ONOS topology event matrics timestemp: {}".format( str( metricsResult ) ) )
 
@@ -230,12 +225,12 @@
                 metricsResult = temp
 
             addingHostTime.append( float( metricsResult ) - tsharkResultTime )
-            main.log.info( "Result of this iteration: {}".format( str( float( metricsResult ) - tsharkResultTime) ) )
+            main.log.info( "Result of this iteration: {}".format( str( float( metricsResult ) - tsharkResultTime ) ) )
             # gethost to remove
-            gethost = main.ONOSrest1.hosts()
+            gethost = main.Cluster.active( 0 ).REST.hosts()
             HosttoRemove = []
             HosttoRemove.append( json.loads( gethost[ 1:len( gethost )-1 ] ).get( 'id' ) )
-            main.CLIs[0].removeHost( HosttoRemove )
+            main.Cluster.active( 0 ).CLI.removeHost( HosttoRemove )
 
         main.log.info( "Result List: {}".format( addingHostTime ) )
 
@@ -244,28 +239,28 @@
         main.log.info( "Average Latency: {}".format( averageResult ) )
 
         # calculate std
-        stdResult = numpy.std(addingHostTime)
-        main.log.info("std: {}".format(stdResult))
+        stdResult = numpy.std( addingHostTime )
+        main.log.info( "std: {}".format( stdResult ) )
 
         # write to DB file
-        main.log.info("Writing results to DS file")
-        with open(main.dbFileName, "a") as dbFile:
+        main.log.info( "Writing results to DS file" )
+        with open( main.dbFileName, "a" ) as dbFile:
             temp = "'" + main.commit + "',"
             temp += "'" + main.nic + "',"
             # Scale number
-            temp += str( main.numCtrls )
+            temp += str( main.Cluster.numCtrls )
             temp += ",'" + "baremetal1" + "'"
             # average latency
             temp += "," + str( averageResult )
             # std of latency
-            temp += "," + str(stdResult)
+            temp += "," + str( stdResult )
             temp += "\n"
             dbFile.write( temp )
 
         assertion = main.TRUE
 
-        utilities.assert_equals(expect=main.TRUE, actual=assertion,
+        utilities.assert_equals( expect=main.TRUE, actual=assertion,
                 onpass='Host latency test successful',
-                onfail='Host latency test failed')
+                onfail='Host latency test failed' )
 
         main.Utils.mininetCleanup( main.Mininet1 )
diff --git a/TestON/tests/SCPF/SCPFhostLat/SCPFhostLat.topo b/TestON/tests/SCPF/SCPFhostLat/SCPFhostLat.topo
index 74e1277..ebde314 100644
--- a/TestON/tests/SCPF/SCPFhostLat/SCPFhostLat.topo
+++ b/TestON/tests/SCPF/SCPFhostLat/SCPFhostLat.topo
@@ -1,109 +1,25 @@
 <TOPOLOGY>
-
     <COMPONENT>
 
-        <ONOSbench>
-            <host>localhost</host>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
             <user>sdn</user>
             <password>rocks</password>
-            <type>OnosDriver</type>
+            <type>OnosClusterDriver</type>
             <connect_order>1</connect_order>
             <COMPONENTS>
-                <home>~/onos</home>
-                <nodes>7</nodes> 
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOSbench>
-
-        <ONOScli1>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>2</connect_order>
-            <COMPONENTS>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost></diff_clihost>  # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
                 <karaf_username></karaf_username>
                 <karaf_password></karaf_password>
-                <prompt></prompt>
+                <web_user></web_user>
+                <web_pass></web_pass>
+                <rest_port></rest_port>
+                <prompt></prompt>  # TODO: we technically need a few of these, one per component
+                <onos_home></onos_home>  # defines where onos home is
+                <nodes> 7 </nodes>  # number of nodes in the cluster
             </COMPONENTS>
-        </ONOScli1>
-
-        <ONOScli2>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>3</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli2>
-
-        <ONOScli3>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>4</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli3>
-
-        <ONOScli4>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>5</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli4>
-
-        <ONOScli5>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>6</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli5>
-
-        <ONOScli6>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>7</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli6>
-
-        <ONOScli7>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>8</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli7>
-
-        <ONOS1>
-            <host>OC1</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>9</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS1>
+        </ONOScell>
 
         <Mininet1>
             <host>OCN</host>
@@ -116,18 +32,6 @@
             </COMPONENTS>
         </Mininet1>
 
-        <ONOSrest1>
-            <host>OC1</host>
-            <port>8181</port>
-            <user>onos</user>
-            <password>rocks</password>
-            <type>OnosRestDriver</type>
-            <connect_order>3</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOSrest1>
-
     </COMPONENT>
 
 </TOPOLOGY>
diff --git a/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.params b/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.params
index 30e6e02..6914862 100644
--- a/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.params
+++ b/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.params
@@ -69,6 +69,13 @@
         <skipReleaseResourcesOnWithdrawal>true</skipReleaseResourcesOnWithdrawal>
         <flowObj>False</flowObj>
     </TEST>
+    <CFG>
+        <intentManager>org.onosproject.net.intent.impl.IntentManager</intentManager>
+        <intentConfigRegi>org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator</intentConfigRegi>
+        <nullProvider>org.onosproject.provider.nil.NullProviders</nullProvider>
+        <linkCollectionIntent>org.onosproject.net.intent.impl.compiler.LinkCollectionIntentObjectiveCompiler</linkCollectionIntent>
+        <intentPerfInstaller>org.onosproject.intentperf.IntentPerfInstaller</intentPerfInstaller>
+    </CFG>
     <DATABASE>
         <dbName>/tmp/IntentEventTPDB</dbName>
         <dbFlowObj>/tmp/IntentEventTPflowObjDB</dbFlowObj>
diff --git a/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.py b/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.py
index 1b55d54..3599806 100644
--- a/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.py
+++ b/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.py
@@ -54,22 +54,26 @@
         main.testSetUp.envSetupDescription()
         stepResult = main.FALSE
         try:
-            main.cellName = main.params[ 'ENV'][ 'cellName']
+            main.cellName = main.params[ 'ENV' ][ 'cellName' ]
             main.apps = main.params[ 'ENV' ][ 'cellApps' ]
             main.BENCHIp = main.params[ 'BENCH' ][ 'ip1' ]
             main.BENCHUser = main.params[ 'BENCH' ][ 'user' ]
             main.MN1Ip = main.params[ 'MN' ][ 'ip1' ]
-            main.maxNodes = int( main.params[ 'max' ] )
-            main.numSwitches = ( main.params[ 'TEST' ][ 'numSwitches' ] ).split(",")
+            main.numSwitches = ( main.params[ 'TEST' ][ 'numSwitches' ] ).split( "," )
             main.skipRelRsrc = main.params[ 'TEST' ][ 'skipReleaseResourcesOnWithdrawal' ]
             main.flowObj = main.params[ 'TEST' ][ 'flowObj' ]
             main.startUpSleep = int( main.params[ 'SLEEP' ][ 'startup' ] )
             main.installSleep = int( main.params[ 'SLEEP' ][ 'install' ] )
             main.verifySleep = int( main.params[ 'SLEEP' ][ 'verify' ] )
-            main.scale = ( main.params[ 'SCALE' ] ).split(",")
+            main.scale = ( main.params[ 'SCALE' ] ).split( "," )
             main.testDuration = main.params[ 'TEST' ][ 'duration' ]
             main.logInterval = main.params[ 'TEST' ][ 'log_interval' ]
             main.debug = main.params[ 'debugMode' ]
+            main.intentManagerCfg = main.params[ 'CFG' ][ 'intentManager' ]
+            main.intentConfigRegiCfg = main.params[ 'CFG' ][ 'intentConfigRegi' ]
+            main.nullProviderCfg = main.params[ 'CFG' ][ 'nullProvider' ]
+            main.linkCollectionIntentCfg = main.params[ 'CFG' ][ 'linkCollectionIntent' ]
+            main.intentPerfInstallerCfg = main.params[ 'CFG' ][ 'intentPerfInstaller' ]
             main.timeout = int( main.params[ 'SLEEP' ][ 'timeout' ] )
             main.cyclePeriod = main.params[ 'TEST' ][ 'cyclePeriod' ]
             if main.flowObj == "True":
@@ -81,7 +85,7 @@
                 main.dbFileName = main.params[ 'DATABASE' ][ 'dbName' ]
                 main.numKeys = main.params[ 'TEST' ][ 'numKeys' ]
 
-            stepResult = main.testSetUp.gitPulling()
+            stepResult = main.testSetUp.envSetup()
             # Create DataBase file
             main.log.info( "Create Database file " + main.dbFileName )
             resultsDB = open( main.dbFileName, "w+" )
@@ -98,75 +102,78 @@
         # Clean up test environment and set up
         import time
         main.maxNumBatch = 0
-        main.testSetUp.getNumCtrls( True )
-        main.testSetUp.envSetup( includeGitPull=False, makeMaxNodes=False )
-        main.testSetUp.ONOSSetUp( main.MN1Ip, True,
-                                  cellName=main.cellName, killRemoveMax=False,
-                                  CtrlsSet=False )
+        main.testSetUp.ONOSSetUp( main.MN1Ip, main.Cluster, True,
+                                  cellName=main.cellName, killRemoveMax=False )
 
         # config apps
-        main.CLIs[0].setCfg( "org.onosproject.net.intent.impl.IntentManager",
-                                  "skipReleaseResourcesOnWithdrawal " + main.skipRelRsrc )
-        main.CLIs[0].setCfg( "org.onosproject.provider.nil.NullProviders", "deviceCount " + str(int( main.numCtrls*10)) )
-        main.CLIs[0].setCfg( "org.onosproject.provider.nil.NullProviders", "topoShape linear" )
-        main.CLIs[0].setCfg( "org.onosproject.provider.nil.NullProviders", "enabled true" )
+        main.Cluster.active( 0 ).CLI.setCfg( main.intentManagerCfg,
+                                             "skipReleaseResourcesOnWithdrawal " + main.skipRelRsrc )
+        main.Cluster.active( 0 ).CLI.setCfg( main.nullProviderCfg,
+                                             "deviceCount " + str( int( main.Cluster.numCtrls * 10 ) ) )
+        main.Cluster.active( 0 ).CLI.setCfg( main.nullProviderCfg, "topoShape linear" )
+        main.Cluster.active( 0 ).CLI.setCfg( main.nullProviderCfg, "enabled true" )
         if main.flowObj:
-            main.CLIs[0].setCfg("org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator",
-                                "useFlowObjectives", value="true")
-            main.CLIs[0].setCfg("org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator",
-                                "defaultFlowObjectiveCompiler",
-                                value='org.onosproject.net.intent.impl.compiler.LinkCollectionIntentObjectiveCompiler')
+            main.Cluster.active( 0 ).CLI.setCfg( main.intentConfigRegiCfg(),
+                                                 "useFlowObjectives", value="true" )
+            main.Cluster.active( 0 ).CLI.setCfg( main.intentConfigRegiCfg(),
+                                                 "defaultFlowObjectiveCompiler",
+                                                 value=main.linkCollectionIntentCfg )
         time.sleep( main.startUpSleep )
 
         # balanceMasters
-        main.CLIs[0].balanceMasters()
+        main.Cluster.active( 0 ).CLI.balanceMasters()
         time.sleep( main.startUpSleep )
 
-    def CASE2(self, main):
+    def CASE2( self, main ):
         import numpy
 
-        main.log.info( "Cluster Count = " + str( main.numCtrls ) )
+        main.log.info( "Cluster Count = " + str( main.Cluster.numCtrls ) )
         # adjust neighbors
-        if main.numCtrls == 1:
+        if main.Cluster.numCtrls == 1:
             main.neighbors = "0"
             main.log.info( "Neighbors: 0" )
         elif main.neighbors != "0":
             main.neighbors = "0"
             main.log.info( "Neighbors: 0" )
         elif main.neighbors == "0":
-            main.neighbors = str( main.numCtrls - 1 )
+            main.neighbors = str( main.Cluster.numCtrls - 1 )
             main.log.info( "Neighbors: " + main.neighbors )
 
         main.log.info( "Config intent-perf app" )
-        main.CLIs[0].setCfg( "org.onosproject.intentperf.IntentPerfInstaller", "numKeys " + main.numKeys )
-        main.CLIs[0].setCfg( "org.onosproject.intentperf.IntentPerfInstaller", "numNeighbors " + str( main.neighbors ) )
-        main.CLIs[0].setCfg( "org.onosproject.intentperf.IntentPerfInstaller", "cyclePeriod " + main.cyclePeriod )
+        main.Cluster.active( 0 ).CLI.setCfg( main.intentPerfInstallerCfg,
+                                             "numKeys " + main.numKeys )
+        main.Cluster.active( 0 ).CLI.setCfg( main.intentPerfInstallerCfg,
+                                             "numNeighbors " + str( main.neighbors ) )
+        main.Cluster.active( 0 ).CLI.setCfg( main.intentPerfInstallerCfg,
+                                             "cyclePeriod " + main.cyclePeriod )
 
-        main.log.info( "Starting intent-perf test for " + str( main.testDuration) + " seconds..." )
-        main.CLIs[0].sendline( "intent-perf-start" )
+        main.log.info( "Starting intent-perf test for " + str( main.testDuration ) + " seconds..." )
+        main.Cluster.active( 0 ).CLI.sendline( "intent-perf-start" )
         stop = time.time() + float( main.testDuration )
 
         while time.time() < stop:
-            time.sleep(15)
-            result = main.CLIs[0].getIntentPerfSummary()
+            time.sleep( 15 )
+            result = main.Cluster.active( 0 ).CLI.getIntentPerfSummary()
             if result:
-                for i in range( main.numCtrls ):
-                    main.log.info( "Node {} Overall Rate: {}".format( main.ONOSip[ i ], result[ main.ONOSip[ i ] ] ) )
+                for ctrl in main.Cluster.active():
+                    main.log.info( "Node {} Overall Rate: {}".format( ctrl.ipAddress,
+                                                                      result[ ctrl.ipAddress ] ) )
         main.log.info( "Stop intent-perf" )
-        for i in range( main.numCtrls ):
-            main.CLIs[i].sendline( "intent-perf-stop" )
+        for ctrl in main.Cluster.active():
+            ctrl.CLI.sendline( "intent-perf-stop" )
         if result:
-            for i in range( main.numCtrls ):
-                main.log.info( "Node {} final Overall Rate: {}".format( main.ONOSip[ i ], result[ main.ONOSip[ i ] ] ) )
+            for ctrl in main.Cluster.active():
+                main.log.info( "Node {} final Overall Rate: {}".format( ctrl.ipAddress,
+                                                                        result[ ctrl.ipAddress ] ) )
 
         with open( main.dbFileName, "a" ) as resultDB:
-            for nodes in range( main.numCtrls ):
+            for nodes in range( main.Cluster.numCtrls ):
                 resultString = "'" + main.commit + "',"
                 resultString += "'1gig',"
-                resultString += str( main.numCtrls) + ","
-                resultString += "'baremetal" + str( nodes+1 ) + "',"
+                resultString += str( main.Cluster.numCtrls ) + ","
+                resultString += "'baremetal" + str( nodes + 1 ) + "',"
                 resultString += main.neighbors + ","
-                resultString += result[ main.ONOSip[ nodes ] ]+","
-                resultString += str(0) + "\n"  # no stddev
+                resultString += result[ main.Cluster.active( nodes ).ipAddress ] + ","
+                resultString += str( 0 ) + "\n"  # no stddev
                 resultDB.write( resultString )
         resultDB.close()
diff --git a/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.topo b/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.topo
index acafd82..93e1e9d 100644
--- a/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.topo
+++ b/TestON/tests/SCPF/SCPFintentEventTp/SCPFintentEventTp.topo
@@ -1,177 +1,25 @@
 <TOPOLOGY>
-
     <COMPONENT>
 
-        <ONOSbench>
-            <host>localhost</host>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
             <user>sdn</user>
             <password>rocks</password>
-            <type>OnosDriver</type>
+            <type>OnosClusterDriver</type>
             <connect_order>1</connect_order>
             <COMPONENTS>
-                <home>~/onos</home>
-                <nodes>7</nodes>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOSbench>
-
-        <ONOScli1>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>2</connect_order>
-            <COMPONENTS>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost></diff_clihost>  # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
                 <karaf_username></karaf_username>
                 <karaf_password></karaf_password>
-                <prompt></prompt>
+                <web_user></web_user>
+                <web_pass></web_pass>
+                <rest_port></rest_port>
+                <prompt></prompt>  # TODO: we technically need a few of these, one per component
+                <onos_home></onos_home>  # defines where onos home is
+                <nodes> 7 </nodes>  # number of nodes in the cluster
             </COMPONENTS>
-        </ONOScli1>
-
-        <ONOScli2>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>3</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli2>
-
-        <ONOScli3>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>4</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli3>
-
-        <ONOScli4>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>5</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli4>
-
-        <ONOScli5>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>6</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli5>
-
-        <ONOScli6>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>7</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli6>
-
-        <ONOScli7>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>8</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli7>
-
-        <ONOS1>
-            <host>OC1</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>9</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS1>
-
-        <ONOS2>
-            <host>OC2</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>10</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS2>
-
-        <ONOS3>
-            <host>OC3</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>11</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS3>
-
-        <ONOS4>
-            <host>OC4</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>12</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS4>
-
-        <ONOS5>
-            <host>OC5</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>13</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS5>
-
-        <ONOS6>
-            <host>OC6</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>14</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS6>
-
-        <ONOS7>
-            <host>OC7</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>15</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS7>
+        </ONOScell>
 
     </COMPONENT>
-
-</TOPOLOGY>
- 
+</TOPOLOGY>
\ No newline at end of file
diff --git a/TestON/tests/SCPF/SCPFintentInstallWithdrawLat/SCPFintentInstallWithdrawLat.params b/TestON/tests/SCPF/SCPFintentInstallWithdrawLat/SCPFintentInstallWithdrawLat.params
index 033c05f..081ebdb 100644
--- a/TestON/tests/SCPF/SCPFintentInstallWithdrawLat/SCPFintentInstallWithdrawLat.params
+++ b/TestON/tests/SCPF/SCPFintentInstallWithdrawLat/SCPFintentInstallWithdrawLat.params
@@ -41,7 +41,12 @@
         # timeout for pexpect
         <timeout>300</timeout>
     </SLEEP>
-
+    <CFG>
+        <intentManager>org.onosproject.net.intent.impl.IntentManager</intentManager>
+        <intentConfigRegi>org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator</intentConfigRegi>
+        <nullProvider>org.onosproject.provider.nil.NullProviders</nullProvider>
+        <linkCollectionIntent>org.onosproject.net.intent.impl.compiler.LinkCollectionIntentObjectiveCompiler</linkCollectionIntent>
+    </CFG>
     <CTRL>
         <USER>sdn</USER>
 
@@ -65,7 +70,6 @@
 
         <ip7>OC7</ip7>
         <port7>6653</port7>
-
     </CTRL>
 
     <MN>
diff --git a/TestON/tests/SCPF/SCPFintentInstallWithdrawLat/SCPFintentInstallWithdrawLat.py b/TestON/tests/SCPF/SCPFintentInstallWithdrawLat/SCPFintentInstallWithdrawLat.py
index a7f7368..f58e715 100644
--- a/TestON/tests/SCPF/SCPFintentInstallWithdrawLat/SCPFintentInstallWithdrawLat.py
+++ b/TestON/tests/SCPF/SCPFintentInstallWithdrawLat/SCPFintentInstallWithdrawLat.py
@@ -65,6 +65,10 @@
             main.startUpSleep = int( main.params[ 'SLEEP' ][ 'startup' ] )
             main.installSleep = int( main.params[ 'SLEEP' ][ 'install' ] )
             main.verifySleep = int( main.params[ 'SLEEP' ][ 'verify' ] )
+            main.intentManagerCfg = main.params[ 'CFG' ][ 'intentManager' ]
+            main.intentConfigRegiCfg = main.params[ 'CFG' ][ 'intentConfigRegi' ]
+            main.nullProviderCfg = main.params[ 'CFG' ][ 'nullProvider' ]
+            main.linkCollectionIntentCfg = main.params[ 'CFG' ][ 'linkCollectionIntent' ]
             main.verifyAttempts = int( main.params[ 'ATTEMPTS' ][ 'verify' ] )
             main.sampleSize = int( main.params[ 'TEST' ][ 'sampleSize' ] )
             main.warmUp = int( main.params[ 'TEST' ][ 'warmUp' ] )
@@ -84,7 +88,7 @@
             for i in range( 0, len( main.intentsList ) ):
                 main.intentsList[ i ] = int( main.intentsList[ i ] )
 
-            stepResult = main.testSetUp.gitPulling()
+            stepResult = main.testSetUp.envSetup()
             # Create DataBase file
             main.log.info( "Create Database file " + main.dbFileName )
             resultsDB = open( main.dbFileName, "w+" )
@@ -98,27 +102,29 @@
         import time
 
         main.maxNumBatch = 0
-        main.testSetUp.getNumCtrls( True )
-        main.testSetUp.envSetup( includeGitPull=False, makeMaxNodes=False )
-        main.testSetUp.ONOSSetUp( main.MN1Ip, True,
-                                  cellName=main.cellName, killRemoveMax=False,
-                                  CtrlsSet=False )
+        main.testSetUp.ONOSSetUp( main.MN1Ip, main.Cluster, True,
+                                  cellName=main.cellName, killRemoveMax=False )
 
         # configure apps
-        main.CLIs[ 0 ].setCfg( "org.onosproject.provider.nil.NullProviders", "deviceCount", value=7 )
-        main.CLIs[ 0 ].setCfg( "org.onosproject.provider.nil.NullProviders", "topoShape", value="linear" )
-        main.CLIs[ 0 ].setCfg( "org.onosproject.provider.nil.NullProviders", "enabled", value="true" )
-        main.CLIs[ 0 ].setCfg( "org.onosproject.net.intent.impl.IntentManager", "skipReleaseResourcesOnWithdrawal", value="true" )
+        main.Cluster.active( 0 ).CLI.setCfg( main.nullProviderCfg,
+                                             "deviceCount", value=7 )
+        main.Cluster.active( 0 ).CLI.setCfg( main.nullProviderCfg,
+                                             "topoShape", value="linear" )
+        main.Cluster.active( 0 ).CLI.setCfg( main.nullProviderCfg,
+                                             "enabled", value="true" )
+        main.Cluster.active( 0 ).CLI.setCfg( main.intentManagerCfg,
+                                             "skipReleaseResourcesOnWithdrawal",
+                                             value="true" )
         if main.flowObj:
-            main.CLIs[ 0 ].setCfg( "org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator",
-                                "useFlowObjectives", value="true" )
-            main.CLIs[ 0 ].setCfg( "org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator",
-                                "defaultFlowObjectiveCompiler",
-                                value='org.onosproject.net.intent.impl.compiler.LinkCollectionIntentObjectiveCompiler' )
+            main.Cluster.active( 0 ).CLI.setCfg( main.intentConfigRegiCfg,
+                                                 "useFlowObjectives", value="true" )
+            main.Cluster.active( 0 ).CLI.setCfg( main.intentConfigRegiCfg,
+                                                 "defaultFlowObjectiveCompiler",
+                                                 value=main.linkCollectionIntentCfg )
         time.sleep( main.startUpSleep )
 
         # balanceMasters
-        main.CLIs[ 0 ].balanceMasters()
+        main.Cluster.active( 0 ).CLI.balanceMasters()
         time.sleep( main.startUpSleep )
 
     def CASE2( self, main ):
@@ -143,9 +149,13 @@
                     main.log.info( "====================Warm Up=====================" )
 
                 # push intents
-                installResult = main.CLIs[ 0 ].pushTestIntents( main.ingress, main.egress, batchSize,
-                                                             offset=1, options="-i", timeout=main.timeout,
-                                                             getResponse=True )
+                installResult = main.Cluster.active( 0 ).CLI.pushTestIntents( main.ingress,
+                                                                              main.egress,
+                                                                              batchSize,
+                                                                              offset=1,
+                                                                              options="-i",
+                                                                              timeout=main.timeout,
+                                                                              getResponse=True )
                 if type( installResult ) is str:
                     if "Failure" in installResult:
                         main.log.error( "Install Intents failure, ignore this iteration." )
@@ -177,9 +187,13 @@
                     continue
                 time.sleep( 2 )
                 # Withdraw Intents
-                withdrawResult = main.CLIs[ 0 ].pushTestIntents( main.ingress, main.egress, batchSize,
-                                                              offset=1, options="-w", timeout=main.timeout,
-                                                              getResponse=True )
+                withdrawResult = main.Cluster.active( 0 ).CLI.pushTestIntents( main.ingress,
+                                                                               main.egress,
+                                                                               batchSize,
+                                                                               offset=1,
+                                                                               options="-w",
+                                                                               timeout=main.timeout,
+                                                                               getResponse=True )
 
                 if type( withdrawResult ) is str:
                     if "Failure" in withdrawResult:
@@ -211,7 +225,7 @@
                     invalidrun += 1
                     continue
                 time.sleep( 2 )
-                main.CLIs[ 0 ].purgeWithdrawnIntents()
+                main.Cluster.active( 0 ).CLI.purgeWithdrawnIntents()
                 validrun += 1
             installave = numpy.average( main.installLatList )
             installstd = numpy.std( main.installLatList )
@@ -219,7 +233,7 @@
             withdrawstd = numpy.std( main.withdrawLatList )
             # log report
             main.log.report( "----------------------------------------------------" )
-            main.log.report( "Scale: " + str( main.numCtrls ) )
+            main.log.report( "Scale: " + str( main.Cluster.numCtrls ) )
             main.log.report( "Intent batch: " + str( batchSize ) )
             main.log.report( "Install average: {}    std: {}".format( installave, installstd ) )
             main.log.report( "Withdraw average: {}   std: {}".format( withdrawave, withdrawstd ) )
@@ -227,7 +241,7 @@
             if not ( numpy.isnan( installave ) or numpy.isnan( installstd ) or\
                     numpy.isnan( withdrawstd ) or numpy.isnan( withdrawave ) ):
                 databaseString = "'" + main.commit + "',"
-                databaseString += str( main.numCtrls ) + ","
+                databaseString += str( main.Cluster.numCtrls ) + ","
                 databaseString += str( batchSize ) + ","
                 databaseString += str( installave ) + ","
                 databaseString += str( installstd ) + ","
diff --git a/TestON/tests/SCPF/SCPFintentInstallWithdrawLat/SCPFintentInstallWithdrawLat.topo b/TestON/tests/SCPF/SCPFintentInstallWithdrawLat/SCPFintentInstallWithdrawLat.topo
index 236d55d..93e1e9d 100644
--- a/TestON/tests/SCPF/SCPFintentInstallWithdrawLat/SCPFintentInstallWithdrawLat.topo
+++ b/TestON/tests/SCPF/SCPFintentInstallWithdrawLat/SCPFintentInstallWithdrawLat.topo
@@ -1,178 +1,25 @@
 <TOPOLOGY>
-
     <COMPONENT>
 
-        <ONOSbench>
-            <host>localhost</host>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
             <user>sdn</user>
             <password>rocks</password>
-            <type>OnosDriver</type>
+            <type>OnosClusterDriver</type>
             <connect_order>1</connect_order>
             <COMPONENTS>
-                <home>~/onos</home>
-                <nodes>7</nodes>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOSbench>
-
-        <ONOScli1>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>2</connect_order>
-            <COMPONENTS>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost></diff_clihost>  # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
                 <karaf_username></karaf_username>
                 <karaf_password></karaf_password>
-                <prompt></prompt>
+                <web_user></web_user>
+                <web_pass></web_pass>
+                <rest_port></rest_port>
+                <prompt></prompt>  # TODO: we technically need a few of these, one per component
+                <onos_home></onos_home>  # defines where onos home is
+                <nodes> 7 </nodes>  # number of nodes in the cluster
             </COMPONENTS>
-        </ONOScli1>
-
-        <ONOScli2>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>3</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli2>
-
-        <ONOScli3>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>4</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli3>
-
-        <ONOScli4>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>5</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli4>
-
-        <ONOScli5>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>6</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli5>
-
-        <ONOScli6>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>7</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli6>
-
-        <ONOScli7>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>8</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli7>
-
-        <ONOS1>
-            <host>OC1</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>9</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS1>
-
-        <ONOS2>
-            <host>OC2</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>10</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS2>
-
-        <ONOS3>
-            <host>OC3</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>11</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS3>
-
-        <ONOS4>
-            <host>OC4</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>12</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS4>
-
-    
-        <ONOS5>
-            <host>OC5</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>13</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS5>
-
-        <ONOS6>
-            <host>OC6</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>14</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS6>
-
-        <ONOS7>
-            <host>OC7</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>15</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS7>
+        </ONOScell>
 
     </COMPONENT>
-
-</TOPOLOGY>
- 
+</TOPOLOGY>
\ No newline at end of file
diff --git a/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.params b/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.params
index 87e3888..3dfbb97 100644
--- a/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.params
+++ b/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.params
@@ -63,7 +63,12 @@
         # timeout for pexpect
         <timeout>300</timeout>
     </SLEEP>
-
+    <CFG>
+        <intentManager>org.onosproject.net.intent.impl.IntentManager</intentManager>
+        <intentConfigRegi>org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator</intentConfigRegi>
+        <nullProvider>org.onosproject.provider.nil.NullProviders</nullProvider>
+        <linkCollectionIntent>org.onosproject.net.intent.impl.compiler.LinkCollectionIntentObjectiveCompiler</linkCollectionIntent>
+    </CFG>
     <CTRL>
         <USER>sdn</USER>
 
diff --git a/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.py b/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.py
index d7acaa1..19a82a4 100644
--- a/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.py
+++ b/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.py
@@ -37,7 +37,7 @@
 
 
 class SCPFintentRerouteLat:
-    def __init__(self):
+    def __init__( self ):
         self.default = ''
 
     def CASE0( self, main ):
@@ -61,14 +61,12 @@
         main.testSetUp.envSetupDescription()
         stepResult = main.FALSE
         try:
-            main.onosIp = main.ONOSbench.getOnosIps()
             main.apps = main.params[ 'ENV' ][ 'cellApps' ]
             main.BENCHUser = main.params[ 'BENCH' ][ 'user' ]
             main.BENCHIp = main.params[ 'BENCH' ][ 'ip1' ]
             main.MN1Ip = main.params[ 'MN' ][ 'ip1' ]
-            main.maxNodes = int( main.params[ 'max' ] )
             main.cellName = main.params[ 'ENV' ][ 'cellName' ]
-            main.scale = ( main.params[ 'SCALE' ] ).split(",")
+            main.scale = ( main.params[ 'SCALE' ] ).split( "," )
             main.timeout = int( main.params[ 'SLEEP' ][ 'timeout' ] )
             main.startUpSleep = int( main.params[ 'SLEEP' ][ 'startup' ] )
             main.installSleep = int( main.params[ 'SLEEP' ][ 'install' ] )
@@ -77,6 +75,10 @@
             main.verifyAttempts = int( main.params[ 'ATTEMPTS' ][ 'verify' ] )
             main.maxInvalidRun = int( main.params[ 'ATTEMPTS' ][ 'maxInvalidRun' ] )
             main.sampleSize = int( main.params[ 'TEST' ][ 'sampleSize' ] )
+            main.intentManagerCfg = main.params[ 'CFG' ][ 'intentManager' ]
+            main.intentConfigRegiCfg = main.params[ 'CFG' ][ 'intentConfigRegi' ]
+            main.nullProviderCfg = main.params[ 'CFG' ][ 'nullProvider' ]
+            main.linkCollectionIntentCfg = main.params[ 'CFG' ][ 'linkCollectionIntent' ]
             main.warmUp = int( main.params[ 'TEST' ][ 'warmUp' ] )
             main.ingress = main.params[ 'TEST' ][ 'ingress' ]
             main.egress = main.params[ 'TEST' ][ 'egress' ]
@@ -95,9 +97,9 @@
                 main.dbFileName = main.params[ 'DATABASE' ][ 'dbName' ]
                 main.intentsList = ( main.params[ 'TEST' ][ 'intents' ] ).split( "," )
 
-            stepResult = main.testSetUp.gitPulling()
+            stepResult = main.testSetUp.envSetup()
 
-            for i in range( 0, len( main.intentsList) ):
+            for i in range( 0, len( main.intentsList ) ):
                 main.intentsList[ i ] = int( main.intentsList[ i ] )
                 # Create DataBase file
             main.log.info( "Create Database file " + main.dbFileName )
@@ -112,6 +114,7 @@
             main.testSetUp.envSetupException( e )
         main.testSetUp.evnSetupConclusion( stepResult )
 
+
     def CASE1( self, main ):
         '''
             clean up test environment and set up
@@ -119,32 +122,30 @@
         import time
 
         main.maxNumBatch = 0
-        main.testSetUp.getNumCtrls( True )
-        main.testSetUp.envSetup( includeGitPull=False, makeMaxNodes=False )
-        main.testSetUp.ONOSSetUp( main.MN1Ip, True,
-                                  cellName=main.cellName, killRemoveMax=False,
-                                  CtrlsSet=False )
+        main.testSetUp.ONOSSetUp( main.MN1Ip, main.Cluster, True,
+                                  cellName=main.cellName, killRemoveMax=False )
         # configure apps
-        main.CLIs[ 0 ].setCfg( "org.onosproject.provider.nil.NullProviders", "deviceCount", value=main.deviceCount )
-        main.CLIs[ 0 ].setCfg( "org.onosproject.provider.nil.NullProviders", "topoShape", value="reroute" )
-        main.CLIs[ 0 ].setCfg( "org.onosproject.provider.nil.NullProviders", "enabled", value="true" )
-        main.CLIs[ 0 ].setCfg( "org.onosproject.net.intent.impl.IntentManager", "skipReleaseResourcesOnWithdrawal", value="true" )
+        main.Cluster.active( 0 ).CLI.setCfg( main.nullProviderCfg, "deviceCount", value=main.deviceCount )
+        main.Cluster.active( 0 ).CLI.setCfg( main.nullProviderCfg, "topoShape", value="reroute" )
+        main.Cluster.active( 0 ).CLI.setCfg( main.nullProviderCfg, "enabled", value="true" )
+        main.Cluster.active( 0 ).CLI.setCfg( main.intentManagerCfg, "skipReleaseResourcesOnWithdrawal",
+                                             value="true" )
         if main.flowObj:
-            main.CLIs[ 0 ].setCfg( "org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator",
-                                "useFlowObjectives", value="true" )
-            main.CLIs[ 0 ].setCfg( "org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator",
-                                "defaultFlowObjectiveCompiler",
-                                value='org.onosproject.net.intent.impl.compiler.LinkCollectionIntentObjectiveCompiler' )
+            main.Cluster.active( 0 ).CLI.setCfg( main.intentConfigRegiCfg,
+                                                 "useFlowObjectives", value="true" )
+            main.Cluster.active( 0 ).CLI.setCfg( main.intentConfigRegiCfg,
+                                                 "defaultFlowObjectiveCompiler",
+                                                 value=main.linkCollectionIntentCfg )
         time.sleep( main.startUpSleep )
-        for i in range( int( main.numCtrls ) ):
-            main.CLIs[i].logSet( "DEBUG", "org.onosproject.metrics.topology" )
-            main.CLIs[i].logSet( "DEBUG", "org.onosproject.metrics.intent" )
+        for ctrl in main.Cluster.active():
+            ctrl.CLI.logSet( "DEBUG", "org.onosproject.metrics.topology" )
+            ctrl.CLI.logSet( "DEBUG", "org.onosproject.metrics.intent" )
         # Balance Master
-        main.CLIs[0].balanceMasters()
+        main.Cluster.active( 0 ).CLI.balanceMasters()
         time.sleep( main.setMasterSleep )
-        if main.numCtrls:
-            main.CLIs[0].deviceRole( main.end1[ 'name' ], main.ONOSip[0] )
-            main.CLIs[0].deviceRole( main.end2[ 'name' ], main.ONOSip[0] )
+        if main.Cluster.numCtrls:
+            main.Cluster.active( 0 ).CLI.deviceRole( main.end1[ 'name' ], main.Cluster.active( 0 ).ipAddress )
+            main.Cluster.active( 0 ).CLI.deviceRole( main.end2[ 'name' ], main.Cluster.active( 0 ).ipAddress )
         time.sleep( main.setMasterSleep )
 
     def CASE2( self, main ):
@@ -154,10 +155,10 @@
         import json
         # from scipy import stats
 
-        print( main.intentsList)
+        print( main.intentsList )
         for batchSize in main.intentsList:
             main.batchSize = batchSize
-            main.log.report("Intent Batch size: " + str(batchSize) + "\n      ")
+            main.log.report( "Intent Batch size: " + str( batchSize ) + "\n      " )
             firstLocalLatencies = []
             lastLocalLatencies = []
             firstGlobalLatencies = []
@@ -167,46 +168,63 @@
             main.invalidRun = 0
             while main.validRun <= main.warmUp + main.sampleSize and main.invalidRun <= main.maxInvalidRun:
                 if main.validRun >= main.warmUp:
-                    main.log.info("================================================")
-                    main.log.info("Valid iteration: {} ".format( main.validRun - main.warmUp) )
-                    main.log.info("Total iteration: {}".format( main.validRun + main.invalidRun) )
-                    main.log.info("================================================")
+                    main.log.info( "================================================" )
+                    main.log.info( "Valid iteration: {} ".format( main.validRun - main.warmUp ) )
+                    main.log.info( "Total iteration: {}".format( main.validRun + main.invalidRun ) )
+                    main.log.info( "================================================" )
                 else:
-                    main.log.info("====================Warm Up=====================")
+                    main.log.info( "====================Warm Up=====================" )
 
                 # push intents
-                main.CLIs[0].pushTestIntents( main.ingress, main.egress, main.batchSize,
-                                             offset=1, options="-i", timeout=main.timeout)
+                main.Cluster.active( 0 ).CLI.pushTestIntents( main.ingress,
+                                                              main.egress,
+                                                              main.batchSize,
+                                                              offset=1,
+                                                              options="-i",
+                                                              timeout=main.timeout )
 
                 # check links, flows and intents
-                main.intentRerouteLatFuncs.sanityCheck( main, main.deviceCount * 2, batchSize * ( main.deviceCount - 1 ), main.batchSize )
+                main.intentRerouteLatFuncs.sanityCheck( main,
+                                                        main.deviceCount * 2,
+                                                        batchSize * ( main.deviceCount - 1 ),
+                                                        main.batchSize )
                 if not main.verify:
                     main.log.warn( "Sanity check failed, skipping this iteration..." )
                     continue
 
                 # Insert one line in karaf.log before link down
-                for i in range( main.numCtrls ):
-                    main.CLIs[ i ].log( "\'Scale: {}, Batch:{}, Iteration: {}\'".format( main.numCtrls, batchSize, main.validRun + main.invalidRun ) )
-
+                main.Cluster.command( "log",
+                                      args=[ "\'Scale: {}, Batch:{}, Iteration: {}\'".format(
+                                          main.Cluster.numCtrls, batchSize, main.validRun + main.invalidRun ) ],
+                                      returnBool=True, specificDriver=2 )
                 # bring link down
-                main.CLIs[0].link( main.end1[ 'port' ], main.end2[ 'port' ], "down",
-                                  timeout=main.timeout, showResponse=False)
+                main.Cluster.active( 0 ).CLI.link( main.end1[ 'port' ], main.end2[ 'port' ], "down",
+                                                   timeout=main.timeout, showResponse=False )
 
                 # check links, flows and intents
-                main.intentRerouteLatFuncs.sanityCheck( main, ( main.deviceCount - 1) * 2, batchSize * main.deviceCount, main.batchSize )
+                main.intentRerouteLatFuncs.sanityCheck( main,
+                                                        ( main.deviceCount - 1 ) * 2,
+                                                        batchSize * main.deviceCount,
+                                                        main.batchSize )
                 if not main.verify:
                     main.log.warn( "Sanity check failed, skipping this iteration..." )
                     continue
 
                 # Get timestamp of last LINK_REMOVED event as separator between iterations
                 skip = False
-                for i in range( main.numCtrls ):
+                for i in range( main.Cluster.numCtrls ):
                     logNum = main.intentRerouteLatFuncs.getLogNum( main, i )
-                    timestamp = str( main.CLIs[ i ].getTimeStampFromLog( "last", "LINK_REMOVED", "time = ", " ", logNum=logNum ) )
+                    timestamp = str( main.Cluster.active( i ).CLI.getTimeStampFromLog( "last",
+                                                                                       "LINK_REMOVED",
+                                                                                       "time = ", " ",
+                                                                                       logNum=logNum ) )
                     if timestamp == main.ERROR:
                         # Try again in case that the log number just increased
                         logNum = main.intentRerouteLatFuncs.getLogNum( main, i )
-                        timestamp = str( main.CLIs[ i ].getTimeStampFromLog( "last", "LINK_REMOVED", "time = ", " ", logNum=logNum ) )
+                        timestamp = str( main.Cluster.active( i ).CLI.getTimeStampFromLog( "last",
+                                                                                           "LINK_REMOVED",
+                                                                                           "time = ", " ",
+                                                                                           logNum=logNum ) )
                     if timestamp == main.ERROR:
                         main.log.warn( "Cannot find the event we want in the log, skipping this iteration..." )
                         main.intentRerouteLatFuncs.bringBackTopology( main )
@@ -218,7 +236,8 @@
                         break
                     else:
                         main.startLine[ i ] = timestamp
-                        main.log.info( "Timestamp of last LINK_REMOVED event on node {} is {}".format( i+1, main.startLine[ i ] ) )
+                        main.log.info( "Timestamp of last LINK_REMOVED event on node {} is {}".format( i + 1,
+                                                                                                       main.startLine[ i ] ) )
                 if skip: continue
 
                 # calculate values
@@ -235,7 +254,8 @@
                 else:
                     main.log.info( "Got valid timestamps" )
 
-                firstLocalLatnecy, lastLocalLatnecy, firstGlobalLatency, lastGlobalLatnecy = main.intentRerouteLatFuncs.calculateLatency( main, topologyTimestamps, intentTimestamps )
+                firstLocalLatnecy, lastLocalLatnecy, firstGlobalLatency, lastGlobalLatnecy \
+                    = main.intentRerouteLatFuncs.calculateLatency( main, topologyTimestamps, intentTimestamps )
                 if firstLocalLatnecy < 0:
                     main.log.info( "Got negative latency, skipping this iteration..." )
                     main.intentRerouteLatFuncs.bringBackTopology( main )
@@ -254,11 +274,17 @@
                 lastGlobalLatencies.append( lastGlobalLatnecy )
 
                 # bring up link and withdraw intents
-                main.CLIs[0].link( main.end1[ 'port' ], main.end2[ 'port' ], "up",
-                                  timeout=main.timeout)
-                main.CLIs[0].pushTestIntents( main.ingress, main.egress, batchSize,
-                                             offset=1, options="-w", timeout=main.timeout)
-                main.CLIs[0].purgeWithdrawnIntents()
+                main.Cluster.active( 0 ).CLI.link( main.end1[ 'port' ],
+                                                   main.end2[ 'port' ],
+                                                   "up",
+                                                   timeout=main.timeout )
+                main.Cluster.active( 0 ).CLI.pushTestIntents( main.ingress,
+                                                              main.egress,
+                                                              batchSize,
+                                                              offset=1,
+                                                              options="-w",
+                                                              timeout=main.timeout )
+                main.Cluster.active( 0 ).CLI.purgeWithdrawnIntents()
 
                 # check links, flows and intents
                 main.intentRerouteLatFuncs.sanityCheck( main, main.deviceCount * 2, 0, 0 )
@@ -270,7 +296,7 @@
             stdLocalLatency = numpy.std( lastLocalLatencies )
             stdGlobalLatency = numpy.std( lastGlobalLatencies )
 
-            main.log.report( "Scale: " + str( main.numCtrls ) + "  \tIntent batch: " + str( batchSize ) )
+            main.log.report( "Scale: " + str( main.Cluster.numCtrls ) + "  \tIntent batch: " + str( batchSize ) )
             main.log.report( "Local latency average:................" + str( aveLocalLatency ) )
             main.log.report( "Global latency average:................" + str( aveGlobalLatency ) )
             main.log.report( "Local latency std:................" + str( stdLocalLatency ) )
@@ -281,7 +307,7 @@
                 # check if got NaN for result
                 resultsDB = open( main.dbFileName, "a" )
                 resultsDB.write( "'" + main.commit + "'," )
-                resultsDB.write( str( main.numCtrls ) + "," )
+                resultsDB.write( str( main.Cluster.numCtrls ) + "," )
                 resultsDB.write( str( batchSize ) + "," )
                 resultsDB.write( str( aveLocalLatency ) + "," )
                 resultsDB.write( str( stdLocalLatency ) + "\n" )
diff --git a/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.topo b/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.topo
index dfe92bc..93e1e9d 100644
--- a/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.topo
+++ b/TestON/tests/SCPF/SCPFintentRerouteLat/SCPFintentRerouteLat.topo
@@ -1,178 +1,25 @@
 <TOPOLOGY>
-
     <COMPONENT>
 
-        <ONOSbench>
-            <host>localhost</host>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
             <user>sdn</user>
             <password>rocks</password>
-            <type>OnosDriver</type>
+            <type>OnosClusterDriver</type>
             <connect_order>1</connect_order>
             <COMPONENTS>
-                <home>~/onos</home>
-                <nodes>7</nodes> 
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOSbench>
-
-        <ONOScli1>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>2</connect_order>
-            <COMPONENTS>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost></diff_clihost>  # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
                 <karaf_username></karaf_username>
                 <karaf_password></karaf_password>
-                <prompt></prompt>
+                <web_user></web_user>
+                <web_pass></web_pass>
+                <rest_port></rest_port>
+                <prompt></prompt>  # TODO: we technically need a few of these, one per component
+                <onos_home></onos_home>  # defines where onos home is
+                <nodes> 7 </nodes>  # number of nodes in the cluster
             </COMPONENTS>
-        </ONOScli1>
-
-        <ONOScli2>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>3</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli2>
-
-        <ONOScli3>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>4</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli3>
-
-        <ONOScli4>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>5</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli4>
-
-        <ONOScli5>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>6</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli5>
-
-        <ONOScli6>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>7</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli6>
-
-        <ONOScli7>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>8</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli7>
-
-        <ONOS1>
-            <host>OC1</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>9</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS1>
-
-        <ONOS2>
-            <host>OC2</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>10</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS2>
-
-        <ONOS3>
-            <host>OC3</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>11</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS3>
-
-        <ONOS4>
-            <host>OC4</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>12</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS4>
-
-    
-        <ONOS5>
-            <host>OC5</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>13</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS5>
-
-        <ONOS6>
-            <host>OC6</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>14</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS6>
-
-        <ONOS7>
-            <host>OC7</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>15</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS7>
+        </ONOScell>
 
     </COMPONENT>
-
-</TOPOLOGY>
- 
+</TOPOLOGY>
\ No newline at end of file
diff --git a/TestON/tests/SCPF/SCPFintentRerouteLat/dependencies/intentRerouteLatFuncs.py b/TestON/tests/SCPF/SCPFintentRerouteLat/dependencies/intentRerouteLatFuncs.py
index 1f830ad..d9d0271 100644
--- a/TestON/tests/SCPF/SCPFintentRerouteLat/dependencies/intentRerouteLatFuncs.py
+++ b/TestON/tests/SCPF/SCPFintentRerouteLat/dependencies/intentRerouteLatFuncs.py
@@ -20,7 +20,7 @@
     intentNum = 0
     while attemps <= main.verifyAttempts:
         time.sleep(main.verifySleep)
-        summary = json.loads( main.CLIs[0].summary( timeout=main.timeout ) )
+        summary = json.loads( main.Cluster.active( 0 ).CLI.summary( timeout=main.timeout ) )
         linkNum = summary.get("links")
         flowNum = summary.get("flows")
         intentNum = summary.get("intents")
@@ -41,18 +41,32 @@
 
 def bringBackTopology( main ):
     main.log.info( "Bring back topology " )
-    main.CLIs[ 0 ].pushTestIntents(main.ingress, main.egress, main.batchSize,
-                                 offset=1, options="-w", timeout=main.timeout)
-    main.CLIs[ 0 ].purgeWithdrawnIntents()
-    main.CLIs[ 0 ].setCfg( "org.onosproject.provider.nil.NullProviders", "deviceCount", value=0)
-    main.CLIs[ 0 ].setCfg( "org.onosproject.provider.nil.NullProviders", "enabled", value="false")
-    main.CLIs[ 0 ].setCfg( "org.onosproject.provider.nil.NullProviders", "deviceCount", value=main.deviceCount)
-    main.CLIs[ 0 ].setCfg( "org.onosproject.provider.nil.NullProviders", "enabled", value="true")
-    main.CLIs[ 0 ].balanceMasters()
+    main.Cluster.active( 0 ).CLI.pushTestIntents( main.ingress,
+                                                  main.egress,
+                                                  main.batchSize,
+                                                  offset=1,
+                                                  options="-w",
+                                                  timeout=main.timeout)
+    main.Cluster.active( 0 ).CLI.purgeWithdrawnIntents()
+    main.Cluster.active( 0 ).CLI.setCfg( main.nullProviderCfg,
+                                         "deviceCount",
+                                         value=0)
+    main.Cluster.active( 0 ).CLI.setCfg( main.nullProviderCfg,
+                                         "enabled",
+                                         value="false")
+    main.Cluster.active( 0 ).CLI.setCfg( main.nullProviderCfg,
+                                         "deviceCount",
+                                         value=main.deviceCount)
+    main.Cluster.active( 0 ).CLI.setCfg( main.nullProviderCfg,
+                                         "enabled",
+                                         value="true")
+    main.Cluster.active( 0 ).CLI.balanceMasters()
     time.sleep( main.setMasterSleep )
-    if len( main.ONOSip ) > 1:
-        main.CLIs[ 0 ].deviceRole(main.end1[ 'name' ], main.ONOSip[ 0 ])
-        main.CLIs[ 0 ].deviceRole(main.end2[ 'name' ], main.ONOSip[ 0 ])
+    if main.Cluster.numCtrls > 1:
+        main.Cluster.active( 0 ).CLI.deviceRole( main.end1[ 'name' ],
+                                                 main.Cluster.active( 0 ).ipAddress )
+        main.Cluster.active( 0 ).CLI.deviceRole( main.end2[ 'name' ],
+                                                 main.Cluster.active( 0 ).ipAddress )
     time.sleep( main.setMasterSleep )
 
 def getLogNum( main, nodeId ):
@@ -60,7 +74,7 @@
     Return the number of karaf log files
     '''
     try:
-        logNameList = main.ONOSbench.listLog( main.onosIp[ nodeId ] )
+        logNameList = main.ONOSbench.listLog( main.Cluster.active( nodeId ).ipAddress )
         assert logNameList is not None
         # FIXME: are two karaf logs enough to cover the events we want?
         if len( logNameList ) >= 2:
@@ -75,9 +89,12 @@
     Get timestamps for the last topology events on all cluster nodes
     '''
     timestamps = []
-    for i in range( main.numCtrls ):
+    for i in range( main.Cluster.numCtrls ):
         # Search for last topology event in karaf log
-        lines = main.CLIs[ i ].logSearch( mode='last', searchTerm=main.searchTerm[ "TopologyTime" ], startLine=main.startLine[ i ], logNum=getLogNum( main, i ) )
+        lines = main.Cluster.active( i ).CLI.logSearch( mode='last',
+                                                        searchTerm=main.searchTerm[ "TopologyTime" ],
+                                                        startLine=main.startLine[ i ],
+                                                        logNum=getLogNum( main, i ) )
         if lines is None or len( lines ) != 1:
             main.log.error( "Error when trying to get topology event timestamp" )
             return main.ERROR
@@ -96,9 +113,12 @@
     Get timestamps for all intent keys on all cluster nodes
     '''
     timestamps = {}
-    for i in range( main.numCtrls ):
+    for i in range( main.Cluster.numCtrls ):
         # Search for intent INSTALLED event in karaf log
-        lines = main.CLIs[ i ].logSearch( mode='all', searchTerm=main.searchTerm[ "InstallTime" ], startLine=main.startLine[ i ], logNum=getLogNum( main, i ) )
+        lines = main.Cluster.active( i ).CLI.logSearch( mode='all',
+                                                        searchTerm=main.searchTerm[ "InstallTime" ],
+                                                        startLine=main.startLine[ i ],
+                                                        logNum=getLogNum( main, i ) )
         if lines is None or len( lines ) == 0:
             main.log.error( "Error when trying to get intent key or timestamp" )
             return main.ERROR
diff --git a/TestON/tests/SCPF/SCPFportLat/SCPFportLat.params b/TestON/tests/SCPF/SCPFportLat/SCPFportLat.params
index 6dbd03d..65e02fa 100644
--- a/TestON/tests/SCPF/SCPFportLat/SCPFportLat.params
+++ b/TestON/tests/SCPF/SCPFportLat/SCPFportLat.params
@@ -23,7 +23,9 @@
         <pull>False</pull>
         <branch>master</branch>
     </GIT>
-
+    <CFG>
+        <defaultTopo>org.onosproject.net.topology.impl.DefaultTopologyProvider</defaultTopo>
+    </CFG>
     <TSHARK>
         <tsharkReusltPath>/tmp/tshark_portStatus</tsharkReusltPath>
         <ofpPortStatus>OF 1.3 146</ofpPortStatus>
diff --git a/TestON/tests/SCPF/SCPFportLat/SCPFportLat.py b/TestON/tests/SCPF/SCPFportLat/SCPFportLat.py
index 437d57b..c1dd9cc 100644
--- a/TestON/tests/SCPF/SCPFportLat/SCPFportLat.py
+++ b/TestON/tests/SCPF/SCPFportLat/SCPFportLat.py
@@ -69,6 +69,7 @@
             main.startUpSleep = int( main.params[ 'SLEEP' ][ 'startup' ] )
             main.measurementSleep = int( main.params[ 'SLEEP' ][ 'measure' ] )
             main.maxScale = int( main.params[ 'max' ] )
+            main.defaultTopoCfg = main.params[ 'CFG' ][ 'defaultTopo' ]
             main.interface = main.params[ 'TEST' ][ 'interface' ]
             main.timeout = int( main.params[ 'TIMEOUT' ][ 'timeout' ] )
             main.MNSleep = int( main.params[ 'SLEEP' ][ 'mininet' ] )
@@ -80,7 +81,7 @@
             else:
                 main.debug = False
 
-            stepResult = main.testSetUp.gitPulling()
+            stepResult = main.testSetUp.envSetup()
             main.log.info( "Create Database file " + main.dbFileName )
             resultsDB = open( main.dbFileName, "w+" )
             resultsDB.close()
@@ -97,25 +98,22 @@
     def CASE1( self, main ):
         # Clean up test environment and set up
         import time
-        main.testSetUp.getNumCtrls( True )
-        main.testSetUp.envSetup( includeGitPull=False, makeMaxNodes=False )
-        main.testSetUp.ONOSSetUp( main.Mininet1, True,
-                                  cellName=main.cellName, killRemoveMax=False,
-                                  CtrlsSet=False )
+        main.testSetUp.ONOSSetUp( main.Mininet1, main.Cluster, True,
+                                  cellName=main.cellName, killRemoveMax=False )
 
         main.log.info( "Configure apps" )
-        main.CLIs[0].setCfg( "org.onosproject.net.topology.impl.DefaultTopologyProvider",
-                            "maxEvents 1" )
-        main.CLIs[0].setCfg( "org.onosproject.net.topology.impl.DefaultTopologyProvider",
-                            "maxBatchMs 0" )
-        main.CLIs[0].setCfg( "org.onosproject.net.topology.impl.DefaultTopologyProvider",
-                            "maxIdleMs 0" )
-        time.sleep(1)
+        main.Cluster.active( 0 ).CLI.setCfg( main.defaultTopoCfg,
+                                                  "maxEvents 1" )
+        main.Cluster.active( 0 ).CLI.setCfg( main.defaultTopoCfg,
+                                                  "maxBatchMs 0" )
+        main.Cluster.active( 0 ).CLI.setCfg( main.defaultTopoCfg,
+                                                  "maxIdleMs 0" )
+        time.sleep( 1 )
         main.log.info( "Copy topology file to Mininet" )
         main.ONOSbench.copyMininetFile( main.topoName,
                                        main.dependencyPath,
                                        main.Mininet1.user_name,
-                                       main.Mininet1.ip_address)
+                                       main.Mininet1.ip_address )
         try:
             from tests.dependencies.utils import Utils
         except ImportError:
@@ -130,39 +128,61 @@
         main.log.info( "Start new mininet topology" )
         main.Mininet1.startNet()
         main.log.info( "Assign switch to controller to ONOS node 1" )
-        time.sleep(1)
-        main.Mininet1.assignSwController( sw='s1', ip=main.ONOSip[0] )
-        main.Mininet1.assignSwController( sw='s2', ip=main.ONOSip[0] )
+        time.sleep( 1 )
+        main.Mininet1.assignSwController( sw='s1',
+                                          ip=main.Cluster.active( 0 ).ipAddress )
+        main.Mininet1.assignSwController( sw='s2',
+                                          ip=main.Cluster.active( 0 ).ipAddress )
 
-        time.sleep(2)
+        time.sleep( 2 )
 
     def CASE2( self, main ):
         import time
         import numpy
         # dictionary for each node and each timestamps
-        resultDict = {'up' : {}, 'down' : {}}
+        resultDict = { 'up' : {}, 'down' : {} }
         for d in resultDict:
-            for i in range( 1, main.numCtrls + 1 ):
-                resultDict[d][ 'node' + str(i) ] = {}
-                resultDict[d][ 'node' + str(i) ][ 'Ave' ] = {}
-                resultDict[d][ 'node' + str(i) ][ 'Std' ] = {}
-                resultDict[d][ 'node' + str(i) ][ 'EtoE' ] = []
-                resultDict[d][ 'node' + str(i) ][ 'PtoD' ] = []
-                resultDict[d][ 'node' + str(i) ][ 'DtoL' ] = []
-                resultDict[d][ 'node' + str(i) ][ 'LtoG' ] = []
+            for i in range( 1, main.Cluster.numCtrls + 1 ):
+                resultDict[ d ][ 'node' + str( i ) ] = {}
+                resultDict[ d ][ 'node' + str( i ) ][ 'Ave' ] = {}
+                resultDict[ d ][ 'node' + str( i ) ][ 'Std' ] = {}
+                resultDict[ d ][ 'node' + str( i ) ][ 'EtoE' ] = []
+                resultDict[ d ][ 'node' + str( i ) ][ 'PtoD' ] = []
+                resultDict[ d ][ 'node' + str( i ) ][ 'DtoL' ] = []
+                resultDict[ d ][ 'node' + str( i ) ][ 'LtoG' ] = []
         for i in range( 1, main.sampleSize + main.warmUp ):
             main.log.info( "==========================================" )
-            main.log.info( "================iteration:{}==============".format(str (i) ) )
+            main.log.info( "================iteration:{}==============".format( str ( i ) ) )
             if i > main.warmUp:
                 # Portdown iteration
-                main.portFunc.capturePortStatusPack( main, main.device, main.interface, "down", resultDict, False )
-                time.sleep(2)
+                main.portFunc.capturePortStatusPack( main,
+                                                     main.device,
+                                                     main.interface,
+                                                     "down",
+                                                     resultDict,
+                                                     False )
+                time.sleep( 2 )
                 # PortUp iteration
-                main.portFunc.capturePortStatusPack( main, main.device, main.interface, "up", resultDict, False )
+                main.portFunc.capturePortStatusPack( main,
+                                                     main.device,
+                                                     main.interface,
+                                                     "up",
+                                                     resultDict,
+                                                     False )
             else:
                 # if warm up, keep old result dictionary
-                main.portFunc.capturePortStatusPack( main, main.device, main.interface, "down", resultDict, True)
-                main.portFunc.capturePortStatusPack( main, main.device, main.interface, "up", resultDict, True)
+                main.portFunc.capturePortStatusPack( main,
+                                                     main.device,
+                                                     main.interface,
+                                                     "down",
+                                                     resultDict,
+                                                     True )
+                main.portFunc.capturePortStatusPack( main,
+                                                     main.device,
+                                                     main.interface,
+                                                     "up",
+                                                     resultDict,
+                                                     True )
 
         # Dictionary for result
         maxDict  = {}
@@ -174,45 +194,45 @@
         maxDict[ 'up' ][ 'node' ] = 0
         EtoEtemp = 0
         for d in resultDict:
-            for i in range( 1, main.numCtrls + 1 ):
+            for i in range( 1, main.Cluster.numCtrls + 1 ):
                 # calculate average and std for result, and grep the max End to End data
-                EtoEtemp = numpy.average( resultDict[d][ 'node' + str(i) ][ 'EtoE' ] )
-                resultDict[d][ 'node' + str(i) ][ 'Ave' ][ 'EtoE' ] = EtoEtemp
-                if maxDict[d][ 'max' ] < EtoEtemp:
+                EtoEtemp = numpy.average( resultDict[ d ][ 'node' + str( i ) ][ 'EtoE' ] )
+                resultDict[ d ][ 'node' + str( i ) ][ 'Ave' ][ 'EtoE' ] = EtoEtemp
+                if maxDict[ d ][ 'max' ] < EtoEtemp:
                     # get max End to End latency
-                    maxDict[d][ 'max' ] = EtoEtemp
-                    maxDict[d][ 'node' ] = i
-                resultDict[d][ 'node' + str(i)][ 'Ave' ][ 'PtoD' ] = numpy.average(resultDict[d][ 'node' + str(i)][ 'PtoD' ] )
-                resultDict[d][ 'node' + str(i)][ 'Ave' ][ 'DtoL' ] = numpy.average(resultDict[d][ 'node' + str(i)][ 'DtoL' ] )
-                resultDict[d][ 'node' + str(i)][ 'Ave' ][ 'LtoG' ] = numpy.average(resultDict[d][ 'node' + str(i)][ 'LtoG' ] )
+                    maxDict[ d ][ 'max' ] = EtoEtemp
+                    maxDict[ d ][ 'node' ] = i
+                resultDict[ d ][ 'node' + str( i ) ][ 'Ave' ][ 'PtoD' ] = numpy.average( resultDict[ d ][ 'node' + str( i ) ][ 'PtoD' ] )
+                resultDict[ d ][ 'node' + str( i ) ][ 'Ave' ][ 'DtoL' ] = numpy.average( resultDict[ d ][ 'node' + str( i ) ][ 'DtoL' ] )
+                resultDict[ d ][ 'node' + str( i ) ][ 'Ave' ][ 'LtoG' ] = numpy.average( resultDict[ d ][ 'node' + str( i ) ][ 'LtoG' ] )
 
-                resultDict[d][ 'node' + str(i)][ 'Std' ][ 'EtoE' ] = numpy.std(resultDict[d][ 'node' + str(i)][ 'EtoE' ] )
-                resultDict[d][ 'node' + str(i)][ 'Std' ][ 'PtoD' ] = numpy.std(resultDict[d][ 'node' + str(i)][ 'PtoD' ] )
-                resultDict[d][ 'node' + str(i)][ 'Std' ][ 'DtoL' ] = numpy.std(resultDict[d][ 'node' + str(i)][ 'DtoL' ] )
-                resultDict[d][ 'node' + str(i)][ 'Std' ][ 'LtoG' ] = numpy.std(resultDict[d][ 'node' + str(i)][ 'LtoG' ] )
+                resultDict[ d ][ 'node' + str( i ) ][ 'Std' ][ 'EtoE' ] = numpy.std( resultDict[ d ][ 'node' + str( i ) ][ 'EtoE' ] )
+                resultDict[ d ][ 'node' + str( i ) ][ 'Std' ][ 'PtoD' ] = numpy.std( resultDict[ d ][ 'node' + str( i ) ][ 'PtoD' ] )
+                resultDict[ d ][ 'node' + str( i ) ][ 'Std' ][ 'DtoL' ] = numpy.std( resultDict[ d ][ 'node' + str( i ) ][ 'DtoL' ] )
+                resultDict[ d ][ 'node' + str( i ) ][ 'Std' ][ 'LtoG' ] = numpy.std( resultDict[ d ][ 'node' + str( i ) ][ 'LtoG' ] )
 
-                main.log.report( "=====node{} Summary:=====".format( str(i) ) )
-                main.log.report( "=============Port {}=======".format( str(d) ) )
+                main.log.report( "=====node{} Summary:=====".format( str( i ) ) )
+                main.log.report( "=============Port {}=======".format( str( d ) ) )
                 main.log.report(
-                    "End to End average: {}".format( str(resultDict[d][ 'node' + str(i) ][ 'Ave' ][ 'EtoE' ] ) ) )
+                    "End to End average: {}".format( str( resultDict[ d ][ 'node' + str( i ) ][ 'Ave' ][ 'EtoE' ] ) ) )
                 main.log.report(
-                    "End to End Std: {}".format( str(resultDict[d][ 'node' + str(i) ][ 'Std' ][ 'EtoE' ] ) ) )
+                    "End to End Std: {}".format( str( resultDict[ d ][ 'node' + str( i ) ][ 'Std' ][ 'EtoE' ] ) ) )
                 main.log.report(
-                    "Package to Device average: {}".format( str(resultDict[d][ 'node' + str(i)][ 'Ave' ][ 'PtoD' ] ) ) )
+                    "Package to Device average: {}".format( str( resultDict[ d ][ 'node' + str( i ) ][ 'Ave' ][ 'PtoD' ] ) ) )
                 main.log.report(
-                    "Package to Device Std: {}".format( str( resultDict[d][ 'node' + str(i)][ 'Std' ][ 'PtoD' ] ) ))
+                    "Package to Device Std: {}".format( str( resultDict[ d ][ 'node' + str( i ) ][ 'Std' ][ 'PtoD' ] ) ) )
                 main.log.report(
-                    "Device to Link average: {}".format( str( resultDict[d][ 'node' + str(i)][ 'Ave' ][ 'DtoL' ] ) ) )
+                    "Device to Link average: {}".format( str( resultDict[ d ][ 'node' + str( i ) ][ 'Ave' ][ 'DtoL' ] ) ) )
                 main.log.report(
-                    "Device to Link Std: {}".format( str( resultDict[d][ 'node' + str(i)][ 'Std' ][ 'DtoL' ] ) ))
+                    "Device to Link Std: {}".format( str( resultDict[ d ][ 'node' + str( i ) ][ 'Std' ][ 'DtoL' ] ) ) )
                 main.log.report(
-                    "Link to Grapg average: {}".format( str( resultDict[d][ 'node' + str(i)][ 'Ave' ][ 'LtoG' ] ) ) )
+                    "Link to Grapg average: {}".format( str( resultDict[ d ][ 'node' + str( i ) ][ 'Ave' ][ 'LtoG' ] ) ) )
                 main.log.report(
-                    "Link to Grapg Std: {}".format( str( resultDict[d][ 'node' + str(i)][ 'Std' ][ 'LtoG' ] ) ) )
+                    "Link to Grapg Std: {}".format( str( resultDict[ d ][ 'node' + str( i ) ][ 'Std' ][ 'LtoG' ] ) ) )
 
         with open( main.dbFileName, "a" ) as dbFile:
             # Scale number
-            temp = str( main.numCtrls )
+            temp = str( main.Cluster.numCtrls )
             temp += ",'baremetal1'"
             # put result
             temp += "," + str( resultDict[ 'up' ][ 'node' + str( maxDict[ 'up' ][ 'node' ] ) ][ 'Ave' ][ 'EtoE' ] )
diff --git a/TestON/tests/SCPF/SCPFportLat/SCPFportLat.topo b/TestON/tests/SCPF/SCPFportLat/SCPFportLat.topo
index c840e11..684335a 100644
--- a/TestON/tests/SCPF/SCPFportLat/SCPFportLat.topo
+++ b/TestON/tests/SCPF/SCPFportLat/SCPFportLat.topo
@@ -2,104 +2,32 @@
 
     <COMPONENT>
 
-        <ONOSbench>
-            <host>localhost</host>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
             <user>sdn</user>
             <password>rocks</password>
-            <type>OnosDriver</type>
+            <type>OnosClusterDriver</type>
             <connect_order>1</connect_order>
             <COMPONENTS>
-                <home>~/onos</home>
-                <nodes>7</nodes>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOSbench>
-
-        <ONOScli1>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>2</connect_order>
-            <COMPONENTS>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost></diff_clihost>  # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
                 <karaf_username></karaf_username>
                 <karaf_password></karaf_password>
-                <prompt></prompt>
+                <web_user></web_user>
+                <web_pass></web_pass>
+                <rest_port></rest_port>
+                <prompt></prompt>  # TODO: we technically need a few of these, one per component
+                <onos_home></onos_home>  # defines where onos home is
+                <nodes> 7 </nodes>  # number of nodes in the cluster
             </COMPONENTS>
-        </ONOScli1>
-
-        <ONOScli2>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>3</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli2>
-
-        <ONOScli3>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>4</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli3>
-
-        <ONOScli4>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>5</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli4>
-
-        <ONOScli5>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>6</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli5>
-
-        <ONOScli6>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>7</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli6>
-
-        <ONOScli7>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>8</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli7>
+        </ONOScell>
 
         <Mininet1>
             <host>OCN</host>
             <user>sdn</user>
             <password>rocks</password>
             <type>MininetCliDriver</type>
-            <connect_order>9</connect_order>
+            <connect_order>2</connect_order>
             <COMPONENTS>
                 <arg1> --custom ~/mininet/custom/topo-perf-2sw.py </arg1>
                 <arg2> --topo mytopo</arg2>
diff --git a/TestON/tests/SCPF/SCPFportLat/dependencies/portFunc.py b/TestON/tests/SCPF/SCPFportLat/dependencies/portFunc.py
index 4bbbfe1..f650521 100644
--- a/TestON/tests/SCPF/SCPFportLat/dependencies/portFunc.py
+++ b/TestON/tests/SCPF/SCPFportLat/dependencies/portFunc.py
@@ -57,11 +57,11 @@
         if len( resultText ) > 1:
             tsharkResultTime = int( float( resultText[1] ) * 1000.0 )
             resultFile.close()
-            for i in range( 1, main.numCtrls + 1 ):
+            for i in range( 1, main.Cluster.numCtrls + 1 ):
                 main.log.info( "================================================" )
                 # get onos metrics timestamps
                 try:
-                    response = json.loads( main.CLIs[i - 1].topologyEventsMetrics() )
+                    response = json.loads( main.Cluster.active( i - 1 ).CLI.topologyEventsMetrics() )
                     deviceTime = int( response.get( "topologyDeviceEventTimestamp" ).get( "value" ) )
                     main.log.info( "ONOS{} device Event timestemp: {}".format( i, deviceTime ) )
 
diff --git a/TestON/tests/SCPF/SCPFscaleTopo/SCPFscaleTopo.py b/TestON/tests/SCPF/SCPFscaleTopo/SCPFscaleTopo.py
index 6e45f81..71758f6 100644
--- a/TestON/tests/SCPF/SCPFscaleTopo/SCPFscaleTopo.py
+++ b/TestON/tests/SCPF/SCPFscaleTopo/SCPFscaleTopo.py
@@ -56,10 +56,9 @@
             main.dependencyPath = main.testOnDirectory + \
                                   main.params[ 'DEPENDENCY' ][ 'path' ]
             main.tsharkResultPath = main.params[ 'TsharkPath' ]
-            main.roleRequest = main.params[ 'SearchTerm' ]['roleRequest']
+            main.roleRequest = main.params[ 'SearchTerm' ][ 'roleRequest' ]
             main.multiovs = main.params[ 'DEPENDENCY' ][ 'multiovs' ]
             main.topoName = main.params[ 'TOPOLOGY' ][ 'topology' ]
-            main.numCtrls = int( main.params[ 'CTRL' ][ 'numCtrls' ] )
             main.topoScale = ( main.params[ 'TOPOLOGY' ][ 'scale' ] ).split( "," )
             main.topoScaleSize = len( main.topoScale )
             wrapperFile1 = main.params[ 'DEPENDENCY' ][ 'wrapper1' ]
@@ -74,14 +73,13 @@
             main.MNSleep = int( main.params[ 'SLEEP' ][ 'MNsleep' ] )
             main.pingTimeout = float( main.params[ 'TIMEOUT' ][ 'pingall' ] )
             main.hostDiscover = main.params[ 'TOPOLOGY' ][ 'host' ]
-            main.hostDiscoverSleep = float( main.params['SLEEP']['host'] )
+            main.hostDiscoverSleep = float( main.params[ 'SLEEP' ][ 'host' ] )
             if main.hostDiscover == 'True':
                 main.hostDiscover = True
             else:
                 main.hostDiscover = False
-            main.homeDir = os.path.expanduser('~')
+            main.homeDir = os.path.expanduser( '~' )
             main.hostsData = {}
-            main.activeNodes = []
 
 
             stepResult = main.testSetUp.envSetup()
@@ -99,7 +97,7 @@
 
             main.dbFilePath = main.params[ 'DATABASE' ][ 'dbPath' ]
             main.log.info( "Create Database file " + main.dbFilePath )
-            resultDB = open(main.dbFilePath, 'w+' )
+            resultDB = open( main.dbFilePath, 'w+' )
             resultDB.close()
 
             main.scaleTopoFunction = imp.load_source( wrapperFile2,
@@ -122,7 +120,7 @@
         main.testSetUp.evnSetupConclusion( stepResult )
         main.commit = main.commit.split( " " )[ 1 ]
 
-    def CASE2( self, main):
+    def CASE2( self, main ):
         """
         - Set up cell
             - Create cell file
@@ -145,10 +143,7 @@
         except ( NameError, AttributeError ):
             main.Utils = Utils()
         main.Utils.mininetCleanup( main.Mininet1 )
-        main.testSetUp.ONOSSetUp( main.Mininet1 )
-        main.activeNodes = []
-        for i in range( main.numCtrls ):
-            main.activeNodes.append( i )
+        main.testSetUp.ONOSSetUp( main.Mininet1, main.Cluster )
 
     def CASE10( self, main ):
         """
@@ -159,15 +154,15 @@
         main.caseExplanation = "Starting Mininet with a scalling topology and " +\
                 "comparing topology elements between Mininet and ONOS"
         if main.topoScale:
-            main.currScale = main.topoScale.pop(0)
+            main.currScale = main.topoScale.pop( 0 )
         else: main.log.error( "topology scale is empty" )
-        main.step( "Starting up TORUS %sx%s topology" % (main.currScale, main.currScale) )
+        main.step( "Starting up TORUS %sx%s topology" % ( main.currScale, main.currScale ) )
 
         main.log.info( "Constructing Mininet command" )
         mnCmd = " mn --custom " + main.Mininet1.home + main.multiovs + \
                 " --switch ovsm --topo " + main.topoName + "," + main.currScale + "," + main.currScale
-        for i in range( main.numCtrls ):
-                mnCmd += " --controller remote,ip=" + main.ONOSip[ i ]
+        for ctrl in main.Cluster.runningNodes:
+                mnCmd += " --controller remote,ip=" + ctrl.ipAddress
         stepResult = main.Mininet1.startNet( mnCmd=mnCmd )
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
@@ -178,11 +173,11 @@
 
         time.sleep( main.MNSleep )
         main.log.info( "Clean up Tshark" )
-        with open(main.tsharkResultPath, "w" ) as tshark:
+        with open( main.tsharkResultPath, "w" ) as tshark:
             tshark.write( "" )
         main.log.info( "Starting Tshark capture" )
         main.ONOSbench.tsharkGrep( main.roleRequest, main.tsharkResultPath, grepOptions='-E' )
-        main.CLIs[ 0 ].activateApp( "org.onosproject.openflow" )
+        main.Cluster.active( 0 ).CLI.activateApp( "org.onosproject.openflow" )
         time.sleep( main.MNSleep )
         main.log.info( "Stop Tshark" )
         main.ONOSbench.tsharkStop()
@@ -210,36 +205,35 @@
         # First capture
         for i in range( 3 ):
             # Calculate total time
-            main.allinfo[ 0 ][ 'info' + str( i )][ 'totalTime' ] = main.scaleTopoFunction.getInfoFromLog( main, main.searchTerm[ 'start' ], 'first', main.searchTerm[ 'end' ], 'last', index=i, funcMode='TD' )
+            main.allinfo[ 0 ][ 'info' + str( i ) ][ 'totalTime' ] = main.scaleTopoFunction.getInfoFromLog( main, main.searchTerm[ 'start' ], 'first', main.searchTerm[ 'end' ], 'last', index=i, funcMode='TD' )
             # Calculate switch connection time
-            main.allinfo[ 0 ][ 'info' + str( i )][ 'swConnection' ] = main.scaleTopoFunction.getInfoFromLog( main, main.searchTerm[ 'start' ], 'first', main.searchTerm[ 'start' ], 'last', index=i, funcMode='TD' )
+            main.allinfo[ 0 ][ 'info' + str( i ) ][ 'swConnection' ] = main.scaleTopoFunction.getInfoFromLog( main, main.searchTerm[ 'start' ], 'first', main.searchTerm[ 'start' ], 'last', index=i, funcMode='TD' )
             # Calculate the time from last switch connection to the last role request
-            main.allinfo[ 0 ][ 'info' + str( i )][ 'lastSwToLastRr' ] = main.scaleTopoFunction.compareTimeDiffWithRoleRequest( main, main.searchTerm[ 'start' ], 'last', index=i )
+            main.allinfo[ 0 ][ 'info' + str( i ) ][ 'lastSwToLastRr' ] = main.scaleTopoFunction.compareTimeDiffWithRoleRequest( main, main.searchTerm[ 'start' ], 'last', index=i )
             # Calculate the time from the last role request to the last topology
-            main.allinfo[ 0 ][ 'info' + str( i )][ 'lastRrToLastTopology' ] = main.scaleTopoFunction.compareTimeDiffWithRoleRequest( main, main.searchTerm[ 'end' ], 'last', index=i )
+            main.allinfo[ 0 ][ 'info' + str( i ) ][ 'lastRrToLastTopology' ] = main.scaleTopoFunction.compareTimeDiffWithRoleRequest( main, main.searchTerm[ 'end' ], 'last', index=i )
             # Calculate the disconnecti rate
-            main.allinfo[ 0 ][ 'info' + str( i )][ 'disconnectRate' ] = main.scaleTopoFunction.getInfoFromLog( main, main.searchTerm[ 'Disconnect' ], 'num', main.searchTerm[ 'start' ], 'num', index=i, funcMode='DR' )
+            main.allinfo[ 0 ][ 'info' + str( i ) ][ 'disconnectRate' ] = main.scaleTopoFunction.getInfoFromLog( main, main.searchTerm[ 'Disconnect' ], 'num', main.searchTerm[ 'start' ], 'num', index=i, funcMode='DR' )
         main.log.debug( "The data is " + str( main.allinfo[ 0 ] ) )
 
         main.case( "Verifying topology: TORUS %sx%s" % ( main.currScale, main.currScale ) )
         main.caseExplanation = "Pinging all hosts and comparing topology " +\
                 "elements between Mininet and ONOS"
 
-        main.log.info( "Gathering topology information")
+        main.log.info( "Gathering topology information" )
         time.sleep( main.MNSleep )
         stepResult = main.TRUE
         main.step( "Comparing MN topology to ONOS topology" )
         compareRetry = 0
         while compareRetry < 3:
             #While loop for retry
-            devices = main.topoRelated.getAllDevices( main.numCtrls, False )
-            ports = main.topoRelated.getAllPorts( main.numCtrls, False )
-            links = main.topoRelated.getAllLinks( main.numCtrls, False)
+            devices = main.topoRelated.getAll( "devices" )
+            ports = main.topoRelated.getAll( "ports" )
+            links = main.topoRelated.getAll( "links" )
             mnSwitches = main.Mininet1.getSwitches()
-            mnLinks = main.Mininet1.getLinks(timeout=180)
+            mnLinks = main.Mininet1.getLinks( timeout=180 )
 
-            for controller in range(len(main.activeNodes)):
-                # controllerStr = str( main.activeNodes[controller] + 1 )
+            for controller in range( len( main.Cluster.active() ) ):
                 currentDevicesResult = main.topoRelated.compareDevicePort(
                                                             main.Mininet1, controller,
                                                             mnSwitches,
@@ -253,10 +247,10 @@
             if stepResult:
                 break
             compareRetry += 1
-        utilities.assert_equals(expect=main.TRUE,
-                                actual=stepResult,
-                                onpass=" Topology match Mininet",
-                                onfail="ONOS Topology doesn't match Mininet")
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass=" Topology match Mininet",
+                                 onfail="ONOS Topology doesn't match Mininet")
 
         if stepResult:
             if main.hostDiscover:
@@ -279,10 +273,10 @@
                 else:
                     main.log.warn( "Some hosts ware not discovered by ONOS... Topology doesn't match!" )
                     stepResult = main.FALSE
-                utilities.assert_equals(expect=main.TRUE,
-                                        actual=stepResult,
-                                        onpass=" Topology match Mininet",
-                                        onfail="ONOS Topology doesn't match Mininet")
+                utilities.assert_equals( expect=main.TRUE,
+                                         actual=stepResult,
+                                         onpass=" Topology match Mininet",
+                                         onfail="ONOS Topology doesn't match Mininet")
             main.log.info( "Finished this iteration, continue to scale next topology." )
         else:
             main.log.info( "Clean up and exit TestON. Finished this test." )
@@ -306,9 +300,9 @@
         # Printing purposes
         node = main.deadNode + 1
         main.log.info( "Stopping node %s" % node )
-        stepResult = main.ONOSbench.onosStop( main.ONOSip[ main.deadNode ] )
+        stepResult = main.ONOSbench.onosStop( main.Cluster.active( main.deadNode ).ipAddress )
         main.log.info( "Removing dead node from list of active nodes" )
-        main.activeNodes.pop( main.deadNode )
+        main.Cluster.runningNodes[ main.deadNode ].active = False
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
@@ -322,15 +316,15 @@
 
         main.case("Bring ONOS node 3 up: TORUS %sx%s" % (main.currScale, main.currScale))
         main.caseExplanation = "Bring node 3 back up and balance the masters"
-
+        ctrl = main.Cluster.runningNodes[ main.deadNode ]
         node = main.deadNode + 1
         main.log.info( "Starting node %s" % node )
-        stepResult = main.ONOSbench.onosStart( main.ONOSip[ main.deadNode ] )
+        stepResult = main.ONOSbench.onosStart( ctrl.ipAddress )
         main.log.info( "Starting onos cli" )
-        stepResult = stepResult and main.CLIs[ main.deadNode ].startOnosCli( main.ONOSip[ main.deadNode ] )
-
+        stepResult = stepResult and \
+                     ctrl.CLI.startOnosCli( ctrl.ipAddress )
         main.log.info( "Adding previously dead node to list of active nodes" )
-        main.activeNodes.append( main.deadNode )
+        ctrl.active = True
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
@@ -349,21 +343,19 @@
         main.step( "Balancing Masters" )
 
         stepResult = main.FALSE
-        if main.activeNodes:
-            controller = main.activeNodes[0]
-            stepResult = utilities.retry( main.CLIs[controller].balanceMasters,
+        if main.Cluster.active():
+            stepResult = utilities.retry( main.Cluster.next().CLI.balanceMasters,
                                           main.FALSE,
                                           [],
                                           sleep=3,
                                           attempts=3 )
-
         else:
             main.log.error( "List of active nodes is empty" )
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
                                  onpass="Balance masters was successfull",
                                  onfail="Failed to balance masters")
-        time.sleep(main.balanceSleep)
+        time.sleep( main.balanceSleep )
 
     def CASE1000( self, main ):
         '''
@@ -375,19 +367,43 @@
         # Second capture
         for i in range( 3 ):
             # Calculate total time
-            main.allinfo[ 1 ][ 'info' + str( i )][ 'totalTime' ] = main.scaleTopoFunction.getInfoFromLog( main, main.searchTerm[ 'start' ], 'first', main.searchTerm[ 'end' ], 'last', index=i, funcMode='TD' )
+            main.allinfo[ 1 ][ 'info' + str( i )][ 'totalTime' ] = main.scaleTopoFunction.getInfoFromLog( main,
+                                                                                                          main.searchTerm[ 'start' ],
+                                                                                                          'first',
+                                                                                                          main.searchTerm[ 'end' ],
+                                                                                                          'last',
+                                                                                                          index=i,
+                                                                                                          funcMode='TD' )
             # Compare the total time
             if main.allinfo[ 1 ][ 'info' + str( i ) ][ 'totalTime' ] > slowestTotalTime:
                 slowestTotalTime = main.allinfo[ 1 ][ 'info' + str( i ) ][ 'totalTime' ]
                 slowestNode = i
             # Calculate switch connection time
-            main.allinfo[ 1 ][ 'info' + str( i )][ 'swConnection' ] = main.scaleTopoFunction.getInfoFromLog( main, main.searchTerm[ 'start' ], 'first', main.searchTerm[ 'start' ], 'last', index=i, funcMode='TD' )
+            main.allinfo[ 1 ][ 'info' + str( i )][ 'swConnection' ] = main.scaleTopoFunction.getInfoFromLog( main,
+                                                                                                             main.searchTerm[ 'start' ],
+                                                                                                             'first',
+                                                                                                             main.searchTerm[ 'start' ],
+                                                                                                             'last',
+                                                                                                             index=i,
+                                                                                                             funcMode='TD' )
             # Calculate the time from last switch connection to the last role request
-            main.allinfo[ 1 ][ 'info' + str( i )][ 'lastSwToLastRr' ] = main.scaleTopoFunction.compareTimeDiffWithRoleRequest( main, main.searchTerm[ 'start' ], 'last', index=i )
+            main.allinfo[ 1 ][ 'info' + str( i )][ 'lastSwToLastRr' ] = main.scaleTopoFunction.compareTimeDiffWithRoleRequest( main,
+                                                                                                                               main.searchTerm[ 'start' ],
+                                                                                                                               'last',
+                                                                                                                               index=i )
             # Calculate the time from the last role request to the last topology
-            main.allinfo[ 1 ][ 'info' + str( i )][ 'lastRrToLastTopology' ] = main.scaleTopoFunction.compareTimeDiffWithRoleRequest( main, main.searchTerm[ 'end' ], 'last', index=i )
+            main.allinfo[ 1 ][ 'info' + str( i )][ 'lastRrToLastTopology' ] = main.scaleTopoFunction.compareTimeDiffWithRoleRequest( main,
+                                                                                                                                     main.searchTerm[ 'end' ],
+                                                                                                                                     'last',
+                                                                                                                                     index=i )
             # Calculate the disconnecti rate
-            main.allinfo[ 1 ][ 'info' + str( i )][ 'disconnectRate' ] = main.scaleTopoFunction.getInfoFromLog( main, main.searchTerm[ 'Disconnect' ], 'num', main.searchTerm[ 'start' ],'num', index=i, funcMode='DR' )
+            main.allinfo[ 1 ][ 'info' + str( i )][ 'disconnectRate' ] = main.scaleTopoFunction.getInfoFromLog( main,
+                                                                                                               main.searchTerm[ 'Disconnect' ],
+                                                                                                               'num',
+                                                                                                               main.searchTerm[ 'start' ],
+                                                                                                               'num',
+                                                                                                               index=i,
+                                                                                                               funcMode='DR' )
 
         if ( main.allinfo[ 0 ] != main.allinfo[ 1 ] ):
             main.log.error( "The results of two capture are different!" )
@@ -411,7 +427,7 @@
         main.writeData = 1
         main.case( "Checking logs for errors, warnings, and exceptions" )
         main.log.info( "Error report: \n" )
-        main.ONOSbench.logReport( main.ONOSip[ 0 ],
+        main.ONOSbench.logReport( main.Cluster.active( 0 ).ipAddress,
                                                             [ "INFO",
                                                               "FOLLOWER",
                                                               "WARN",
diff --git a/TestON/tests/SCPF/SCPFscaleTopo/SCPFscaleTopo.topo b/TestON/tests/SCPF/SCPFscaleTopo/SCPFscaleTopo.topo
index 36476f8..2461858 100755
--- a/TestON/tests/SCPF/SCPFscaleTopo/SCPFscaleTopo.topo
+++ b/TestON/tests/SCPF/SCPFscaleTopo/SCPFscaleTopo.topo
@@ -1,59 +1,32 @@
 <TOPOLOGY>
     <COMPONENT>
 
-        <ONOSbench>
-            <host>localhost</host>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
             <user>sdn</user>
             <password>rocks</password>
-            <type>OnosDriver</type>
+            <type>OnosClusterDriver</type>
             <connect_order>1</connect_order>
             <COMPONENTS>
-                <nodes>3</nodes>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOSbench>
-
-        <ONOScli1>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>2</connect_order>
-            <COMPONENTS>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost></diff_clihost>  # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
                 <karaf_username></karaf_username>
                 <karaf_password></karaf_password>
-                <prompt></prompt>
+                <web_user></web_user>
+                <web_pass></web_pass>
+                <rest_port></rest_port>
+                <prompt></prompt>  # TODO: we technically need a few of these, one per component
+                <onos_home></onos_home>  # defines where onos home is
+                <nodes> 3 </nodes>  # number of nodes in the cluster
             </COMPONENTS>
-        </ONOScli1>
-
-        <ONOScli2>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>3</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli2>
-
-         <ONOScli3>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>4</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli3>
+        </ONOScell>
 
         <Mininet1>
             <host>OCN</host>
             <user>sdn</user>
             <password>rocks</password>
             <type>MininetCliDriver</type>
-            <connect_order>5</connect_order>
+            <connect_order>2</connect_order>
             <COMPONENTS>
                 <home>~/mininet/custom/</home>
                 <prompt></prompt>
diff --git a/TestON/tests/SCPF/SCPFscaleTopo/dependencies/scaleTopoFunction.py b/TestON/tests/SCPF/SCPFscaleTopo/dependencies/scaleTopoFunction.py
index 43f80eb..6f2647d 100644
--- a/TestON/tests/SCPF/SCPFscaleTopo/dependencies/scaleTopoFunction.py
+++ b/TestON/tests/SCPF/SCPFscaleTopo/dependencies/scaleTopoFunction.py
@@ -77,7 +77,7 @@
 
     '''
     try:
-        termInfo = main.CLIs[ index ].logSearch( mode=Mode, searchTerm=term )
+        termInfo = main.Cluster.active( index ).CLI.logSearch( mode=Mode, searchTerm=term )
         termTime = getTimestampFromString( main, termInfo[ 0 ] )
         roleRequestTime = getRoleRequestTimeFromTshark( main )
         if termTime == -1 or roleRequestTime == -1:
@@ -108,8 +108,8 @@
 
     '''
     try:
-        termInfo1 = main.CLIs[ index ].logSearch( mode=mode1, searchTerm=term1 )
-        termInfo2 = main.CLIs[ index ].logSearch( mode=mode2, searchTerm=term2 )
+        termInfo1 = main.Cluster.active( index ).CLI.logSearch( mode=mode1, searchTerm=term1 )
+        termInfo2 = main.Cluster.active( index ).CLI.logSearch( mode=mode2, searchTerm=term2 )
         if funcMode == 'TD':
             startTime = getTimestampFromString( main, termInfo1[0] )
             endTime = getTimestampFromString ( main, termInfo2[0] )
@@ -243,78 +243,16 @@
         Compare topology( devices, links, ports, hosts ) between ONOS and
         mininet using sts
     """
-    devices = []
-    links = []
-    ports = []
-    hosts = []
-    switchResult = []
-    linksResult = []
-    portsResult = []
-    hostsResult = []
-    mnSwitches = main.Mininet1.getSwitches()
-    mnLinks = main.Mininet1.getLinks()
-    mnHosts = main.Mininet1.getHosts()
-    compareTopoResult = main.TRUE
-
-    for i in range( main.numCtrls ):
-        devices.append( json.loads( main.CLIs[ i ].devices() ) )
-        links.append( json.loads( main.CLIs[ i ].links() ) )
-        ports.append( json.loads(  main.CLIs[ i ].ports() ) )
-        hosts.append( json.loads( main.CLIs[ i ].hosts() ) )
-
-    # Comparing switches
-    main.log.info( main.topoName + ": Comparing switches in each ONOS nodes" +
-                   " with Mininet" )
-    for i in range( main.numCtrls ):
-        tempResult = main.Mininet1.compareSwitches( mnSwitches,
-                                                    devices[ i ],
-                                                    ports[ i ] )
-        switchResult.append( tempResult )
-        if tempResult == main.FALSE:
-            main.log.error( main.topoName + ": ONOS-" + str( i + 1 ) +
-                            " switch view is incorrect " )
-
-    if all( result == main.TRUE for result in switchResult ):
-        main.log.info( main.topoName + ": Switch view in all ONOS nodes "+
-                       "are correct " )
-    else:
-        compareTopoResult = main.FALSE
-
-    # Comparing links
-    main.log.info( main.topoName + ": Comparing links in each ONOS nodes" +
-                   " with Mininet" )
-    for i in range( main.numCtrls ):
-        tempResult = main.Mininet1.compareLinks( mnSwitches,
-                                                 mnLinks,
-                                                 links[ i ] )
-        linksResult.append( tempResult )
-        if tempResult == main.FALSE:
-            main.log.error( main.topoName + ": ONOS-" + str( i + 1 ) +
-                            " links view are incorrect " )
-
-    if all( result == main.TRUE for result in linksResult ):
-        main.log.info( main.topoName + ": Links view in all ONOS nodes "+
-                       "are correct " )
-    else:
-        compareTopoResult = main.FALSE
-
-    # Comparing hosts
-    main.log.info( main.topoName + ": Comparing hosts in each ONOS nodes" +
-                   " with Mininet" )
-    for i in range( main.numCtrls ):
-        tempResult = main.Mininet1.compareHosts( mnHosts, hosts[ i ] )
-        hostsResult.append( tempResult )
-        if tempResult == main.FALSE:
-            main.log.error( main.topoName + ": ONOS-" + str( i + 1 ) +
-                            " hosts view are incorrect " )
-
-    if all( result == main.TRUE for result in hostsResult ):
-        main.log.info( main.topoName + ": Hosts view in all ONOS nodes "+
-                       "are correct " )
-    else:
-        compareTopoResult = main.FALSE
-
-    return compareTopoResult
+    try:
+        from tests.dependencies.topology import Topology
+    except ImportError:
+        main.log.error( "Topology not found exiting the test" )
+        main.exit()
+    try:
+        main.topoRelated
+    except ( NameError, AttributeError ):
+        main.topoRelated = Topology()
+    return main.topoRelated.compareTopos( main.Mininet1 )
 
 def assignSwitch( main ):
     """
@@ -324,12 +262,12 @@
     assignResult = main.TRUE
     switchList =  main.Mininet1.getSwitch()
     assignResult = main.Mininet1.assignSwController( sw=switchList,
-                                                     ip=main.ONOSip[ 0 ],
+                                                     ip=main.Cluster.active( 0 ).ipAddress,
                                                      port=6633 )
 
     for sw in switchList:
         response = main.Mininet1.getSwController( sw )
-        if re.search( "tcp:" + main.ONOSip[ 0 ], response ):
+        if re.search( "tcp:" + main.Cluster.active( 0 ).ipAddress, response ):
             assignResult = assignResult and main.TRUE
         else:
             assignResult = main.FALSE
@@ -344,15 +282,15 @@
     appCheck = main.TRUE
     getDataResult = main.TRUE
     main.log.info( main.topoName + ": Activating reactive forwarding app " )
-    activateResult = main.CLIs[ 0 ].activateApp( "org.onosproject.fwd" )
+    activateResult = main.Cluster.active( 0 ).activateApp( "org.onosproject.fwd" )
 
     if main.hostsData:
         main.hostsData = {}
-    for i in range( main.numCtrls ):
-        appCheck = appCheck and main.CLIs[ i ].appToIDCheck()
+    for ctrl in main.Cluster.active():
+        appCheck = appCheck and ctrl.CLI.appToIDCheck()
         if appCheck != main.TRUE:
-            main.log.warn( main.CLIs[ i ].apps() )
-            main.log.warn( main.CLIs[ i ].appIDs() )
+            main.log.warn( ctrl.CLI.apps() )
+            main.log.warn( ctrl.CLI.appIDs() )
 
     time.sleep( main.fwdSleep )
 
@@ -362,12 +300,12 @@
                                         acceptableFailed=acceptableFailed )
 
     main.log.info( main.topoName + ": Deactivate reactive forwarding app " )
-    activateResult = main.CLIs[ 0 ].deactivateApp( "org.onosproject.fwd" )
-    for i in range( main.numCtrls ):
-        appCheck = appCheck and main.CLIs[ i ].appToIDCheck()
+    activateResult = main.Cluster.active( 0 ).deactivateApp( "org.onosproject.fwd" )
+    for ctrl in main.Cluster.active():
+        appCheck = appCheck and ctrl.CLI.appToIDCheck()
         if appCheck != main.TRUE:
-            main.log.warn( main.CLIs[ i ].apps() )
-            main.log.warn( main.CLIs[ i ].appIDs() )
+            main.log.warn( ctrl.CLI.apps() )
+            main.log.warn( ctrl.CLI.appIDs() )
 
     return pingResult
 
@@ -379,21 +317,21 @@
     appCheck = main.TRUE
     getDataResult = main.TRUE
     main.log.info( main.topoName + ": Activating reactive forwarding app " )
-    activateResult = main.CLIs[ 0 ].activateApp( "org.onosproject.fwd" )
+    activateResult = main.Cluster.active( 0 ).CLI.activateApp( "org.onosproject.fwd" )
 
     if main.hostsData:
         main.hostsData = {}
-    for i in range( main.numCtrls ):
-        appCheck = appCheck and main.CLIs[ i ].appToIDCheck()
+    for ctrl in main.Cluster.active():
+        appCheck = appCheck and ctrl.CLI.appToIDCheck()
         if appCheck != main.TRUE:
-            main.log.warn( main.CLIs[ i ].apps() )
-            main.log.warn( main.CLIs[ i ].appIDs() )
+            main.log.warn( ctrl.CLI.apps() )
+            main.log.warn( ctrl.CLI.appIDs() )
 
     time.sleep( main.fwdSleep )
     # Discover hosts using pingall
     pingResult = main.Mininet1.pingall( timeout=900 )
 
-    hostsJson = json.loads( main.CLIs[ 0 ].hosts() )
+    hostsJson = json.loads( main.Cluster.active( 0 ).CLI.hosts() )
     hosts = main.Mininet1.getHosts().keys()
 
     for host in hosts:
@@ -419,12 +357,12 @@
         getDataResult = main.FALSE
 
     main.log.info( main.topoName + ": Deactivate reactive forwarding app " )
-    activateResult = main.CLIs[ 0 ].deactivateApp( "org.onosproject.fwd" )
-    for i in range( main.numCtrls ):
-        appCheck = appCheck and main.CLIs[ i ].appToIDCheck()
+    activateResult = main.Cluster.active( 0 ).CLI.deactivateApp( "org.onosproject.fwd" )
+    for ctrl in main.Cluster.active():
+        appCheck = appCheck and ctrl.CLI.appToIDCheck()
         if appCheck != main.TRUE:
-            main.log.warn( main.CLIs[ i ].apps() )
-            main.log.warn( main.CLIs[ i ].appIDs() )
+            main.log.warn( ctrl.CLI.apps() )
+            main.log.warn( ctrl.CLI.appIDs() )
 
     # This data can be use later for intents
     print main.hostsData
@@ -439,65 +377,40 @@
     Return:
         Retruns main.TRUE for a successful restart, main.FALSE otherwise.
     """
-    uninstallResult = []
-    installResult = []
     stopResult = []
     startResult = []
     onosIsUpResult = []
     restartResult = main.TRUE
 
-    main.log.info( main.topoName + ": Uninstall ONOS cluster" )
-    for ip in main.ONOSip:
-        uninstallResult.append( main.ONOSbench.onosUninstall( nodeIp=ip ) )
-
-    if all( result == main.TRUE for result in uninstallResult ):
-        main.log.info( main.topoName + ": Successfully uninstall ONOS cluster" )
-    else:
+    uninstallResult = main.testSetUp.uninstallOnos( main.Cluster, False )
+    if uninstallResult != main.TRUE:
         restartResult = main.FALSE
-        main.log.error( main.topoName + ": Failed to uninstall ONOS cluster" )
-
-    time.sleep( main.startUpSleep )
-
-    main.log.info( main.topoName + ": Installing ONOS cluster" )
-
-    for i in range( main.numCtrls ):
-        installResult.append( main.ONOSbench.onosInstall(
-                                                    node=main.ONOSip[ i ] ) )
-
-    if all( result == main.TRUE for result in installResult ):
-        main.log.info( main.topoName + ": Successfully installed ONOS cluster" )
-    else:
-        restartResult = main.FALSE
-        main.log.error( main.topoName + ": Failed to install ONOS cluster" )
-
-    main.log.info( main.topoName + ": set up ONOS secure SSH" )
-    secureSshResult = []
-    for i in range( int( main.numCtrls ) ):
-        secureSshResult.append( main.onosSecureSSH( node=main.ONOSip[i] ) )
-    if all( result == main.TRUE for result in secureSshResult ):
-        main.log.info( main.topoName + ": Successfully set up ONOS secure SSH" )
-    else:
-        main.log.error( main.topoName + ": Failed to set up ONOS secure SSH" )
+    installResult = main.testSetUp.installOnos( main.Cluster, False )
+    if installResult != main.TRUE:
         restartResult = main.FALSE
 
-    for i in range( main.numCtrls ):
-        onosIsUpResult.append( main.ONOSbench.isup( main.ONOSip[ i ] ) )
+    secureSshResult = main.testSetUp.setupSsh( main.Cluster )
+    if secureSshResult != main.TRUE:
+        restartResult = main.FALSE
+
+    for ctrl in main.Cluster.runningNodes:
+        onosIsUpResult.append( main.ONOSbench.isup( ctrl.ipAddress ) )
 
     if all( result == main.TRUE for result in onosIsUpResult ):
         main.log.report( "ONOS instance is up and ready" )
     else:
         main.log.report( "ONOS instance may not be up, stop and " +
                          "start ONOS again " )
-        for i in range( main.numCtrls ):
-            stopResult.append( main.ONOSbench.onosStop( main.ONOSip[ i ] ) )
+        for ctrl in main.Cluster.runningNodes:
+            stopResult.append( main.ONOSbench.onosStop( ctrl.ipAddress ) )
 
         if all( result == main.TRUE for result in stopResult ):
             main.log.info( main.topoName + ": Successfully stop ONOS cluster" )
         else:
             main.log.error( main.topoName + ": Failed to stop ONOS cluster" )
 
-        for i in range( main.numCtrls ):
-            startResult.append( main.ONOSbench.onosStart( main.ONOSip[ i ] ) )
+        for ctrl in main.Cluster.runningNodes:
+            startResult.append( main.ONOSbench.onosStart( ctrl.ipAddress ) )
 
         if all( result == main.TRUE for result in startResult ):
             main.log.info( main.topoName + ": Successfully start ONOS cluster" )
@@ -505,14 +418,8 @@
             main.log.error( main.topoName + ": Failed to start ONOS cluster" )
 
     main.log.info( main.topoName + ": Starting ONOS CLI" )
-    cliResult = []
-    for i in range( main.numCtrls ):
-        cliResult.append( main.CLIs[ i ].startOnosCli( main.ONOSip[ i ] ) )
-
-    if all( result == main.TRUE for result in cliResult ):
-        main.log.info( main.topoName + ": Successfully start ONOS cli" )
-    else:
-        main.log.error( main.topoName + ": Failed to start ONOS cli" )
+    cliResult = main.testSetUp.startOnosClis( main.Cluster )
+    if cliResult != main.TRUE:
         restartResult = main.FALSE
 
 
diff --git a/TestON/tests/SCPF/SCPFscaleTopo/dependencies/topo.py b/TestON/tests/SCPF/SCPFscaleTopo/dependencies/topo.py
index 74f8751..c5c1f88 100644
--- a/TestON/tests/SCPF/SCPFscaleTopo/dependencies/topo.py
+++ b/TestON/tests/SCPF/SCPFscaleTopo/dependencies/topo.py
@@ -43,7 +43,7 @@
         main.Mininet1.arping( srcHost=hostList, dstHost="10.0.0.1", output=main.FALSE, noResult=True )
     try:
         summaryStr = ""
-        summaryStr = json.loads( main.CLIs[0].summary().encode() )
+        summaryStr = json.loads( main.Cluster.active( 0 ).CLI.summary().encode() )
         hostNum = summaryStr.get( 'hosts' )
 
     except (TypeError, ValueError):
diff --git a/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.params b/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.params
index 4485f0f..5cb65d0 100644
--- a/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.params
+++ b/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.params
@@ -25,6 +25,11 @@
     <TEST>
         <flowObj>False</flowObj>
     </TEST>
+    <CFG>
+        <intentConfigRegi>org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator</intentConfigRegi>
+        <nullProvider>org.onosproject.provider.nil.NullProviders</nullProvider>
+        <linkCollectionIntent>org.onosproject.net.intent.impl.compiler.LinkCollectionIntentObjectiveCompiler</linkCollectionIntent>
+    </CFG>
     <GIT>
         <pull>False</pull>
         <branch>master</branch>
diff --git a/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.py b/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.py
index e7690ba..6eb1c00 100644
--- a/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.py
+++ b/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.py
@@ -36,7 +36,7 @@
     def __init__( self ):
         self.default = ''
 
-    def CASE0( self, main):
+    def CASE0( self, main ):
         import sys
         import json
         import time
@@ -64,6 +64,9 @@
             main.installSleep = int( main.params[ 'SLEEP' ][ 'install' ] )
             main.verifySleep = int( main.params[ 'SLEEP' ][ 'verify' ] )
             main.rerouteSleep = int ( main.params[ 'SLEEP' ][ 'reroute' ] )
+            main.intentConfigRegiCfg = main.params[ 'CFG' ][ 'intentConfigRegi' ]
+            main.nullProviderCfg = main.params[ 'CFG' ][ 'nullProvider' ]
+            main.linkCollectionIntentCfg = main.params[ 'CFG' ][ 'linkCollectionIntent' ]
             main.verifyAttempts = int( main.params[ 'ATTEMPTS' ][ 'verify' ] )
             main.ingress = main.params[ 'LINK' ][ 'ingress' ]
             main.egress = main.params[ 'LINK' ][ 'egress' ]
@@ -86,17 +89,15 @@
             wrapperFile1 = main.params[ 'DEPENDENCY' ][ 'wrapper1' ]
             main.nic = main.params[ 'DATABASE' ][ 'nic' ]
             node = main.params[ 'DATABASE' ][ 'node' ]
-            stepResult = main.testSetUp.gitPulling()
+            stepResult = main.testSetUp.envSetup()
             main.log.info( "Cresting DB file" )
             with open( main.dbFileName, "w+" ) as dbFile:
-                dbFile.write("")
+                dbFile.write( "" )
         except Exception as e:
             main.testSetUp.envSetupException( e )
         main.testSetUp.evnSetupConclusion( stepResult )
         main.commit = main.commit.split( " " )[ 1 ]
     def CASE1( self ):
-        main.testSetUp.getNumCtrls( True )
-        main.testSetUp.envSetup( includeGitPull=False, makeMaxNodes=False )
         copyResult = main.ONOSbench.copyMininetFile( main.topology,
                                                      main.dependencyPath,
                                                      main.Mininet1.user_name,
@@ -109,8 +110,8 @@
         - Install ONOS cluster
         - Connect to cli
         """
-        main.testSetUp.ONOSSetUp( main.Mininet1, True,
-                                  killRemoveMax=False, CtrlsSet=False )
+        main.testSetUp.ONOSSetUp( main.Mininet1, main.Cluster, True,
+                                  killRemoveMax=False )
 
     def CASE10( self, main ):
         """
@@ -118,8 +119,8 @@
         """
         import json
         # Activate apps
-        main.step("Activating null-provider")
-        appStatus = utilities.retry( main.CLIs[0].activateApp,
+        main.step( "Activating null-provider" )
+        appStatus = utilities.retry( main.Cluster.active( 0 ).CLI.activateApp,
                                      main.FALSE,
                                      [ 'org.onosproject.null' ],
                                      sleep=main.verifySleep,
@@ -130,24 +131,24 @@
                                  onfail="Failed activate null-provider" )
 
         # Setup the null-provider
-        main.step("Configuring null-provider")
+        main.step( "Configuring null-provider" )
         cfgStatus = utilities.retry( main.ONOSbench.onosCfgSet,
                                     main.FALSE,
-                                    [ main.ONOSip[0],
-                                      'org.onosproject.provider.nil.NullProviders', 'deviceCount 8' ],
+                                    [ main.Cluster.active( 0 ).ipAddress,
+                                      main.nullProviderCfg, 'deviceCount 8' ],
                                     sleep=main.verifySleep,
                                     attempts = main.verifyAttempts )
         cfgStatus = cfgStatus and utilities.retry( main.ONOSbench.onosCfgSet,
                                                    main.FALSE,
-                                                   [ main.ONOSip[0],
-                                                     'org.onosproject.provider.nil.NullProviders', 'topoShape reroute' ],
+                                                   [ main.Cluster.active( 0 ).ipAddress,
+                                                     main.nullProviderCfg, 'topoShape reroute' ],
                                                    sleep=main.verifySleep,
                                                    attempts = main.verifyAttempts )
 
         cfgStatus = cfgStatus and utilities.retry( main.ONOSbench.onosCfgSet,
                                                    main.FALSE,
-                                                   [ main.ONOSip[0],
-                                                     'org.onosproject.provider.nil.NullProviders', 'enabled true' ],
+                                                   [ main.Cluster.active( 0 ).ipAddress,
+                                                     main.nullProviderCfg, 'enabled true' ],
                                                    sleep=main.verifySleep,
                                                    attempts = main.verifyAttempts )
 
@@ -158,12 +159,12 @@
                                  onfail="Failed to configure null-provider" )
 
         # give onos some time to settle
-        time.sleep(main.startUpSleep)
+        time.sleep( main.startUpSleep )
 
-        main.log.info("Setting default flows to zero")
+        main.log.info( "Setting default flows to zero" )
         main.defaultFlows = 0
 
-        main.step("Check status of null-provider setup")
+        main.step( "Check status of null-provider setup" )
         caseResult = appStatus and cfgStatus
         utilities.assert_equals( expect=main.TRUE,
                                  actual=caseResult,
@@ -176,11 +177,11 @@
         # If the null-provider setup was unsuccessfull, then there is no point to
         # run the subsequent cases
 
-        time.sleep(main.startUpSleep)
+        time.sleep( main.startUpSleep )
         main.step( "Balancing Masters" )
 
         stepResult = main.FALSE
-        stepResult = utilities.retry( main.CLIs[0].balanceMasters,
+        stepResult = utilities.retry( main.Cluster.active( 0 ).CLI.balanceMasters,
                                       main.FALSE,
                                       [],
                                       sleep=3,
@@ -189,51 +190,50 @@
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
                                  onpass="Balance masters was successfull",
-                                 onfail="Failed to balance masters")
+                                 onfail="Failed to balance masters" )
 
         time.sleep( 5 )
         if not caseResult:
             main.setupSkipped = True
 
-    def CASE11( self, main):
+    def CASE11( self, main ):
         '''
             Setting up mininet
         '''
         import json
         import time
- 
         devices = []
-        devices = main.CLIs[0].getAllDevicesId()
+        devices = main.Cluster.active( 0 ).CLI.getAllDevicesId()
         for d in devices:
-            main.CLIs[0].deviceRemove( d )
+            main.Cluster.active( 0 ).CLI.deviceRemove( d )
 
-        time.sleep(main.startUpSleep)
+        time.sleep( main.startUpSleep )
         if main.flowObj:
-            main.CLIs[0].setCfg("org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator",
-                                "useFlowObjectives", value="true")
-            main.CLIs[0].setCfg("org.onosproject.net.intent.impl.compiler.IntentConfigurableRegistrator",
+            main.Cluster.active( 0 ).CLI.setCfg( main.intentConfigRegiCfg,
+                                "useFlowObjectives", value="true" )
+            main.Cluster.active( 0 ).CLI.setCfg( main.intentConfigRegiCfg,
                                 "defaultFlowObjectiveCompiler",
-                                value='org.onosproject.net.intent.impl.compiler.LinkCollectionIntentObjectiveCompiler')
-        main.step('Starting mininet topology')
-        mnStatus = main.Mininet1.startNet(topoFile='~/mininet/custom/rerouteTopo.py')
+                                value=main.linkCollectionIntentCfg )
+        main.step( 'Starting mininet topology' )
+        mnStatus = main.Mininet1.startNet( topoFile='~/mininet/custom/rerouteTopo.py' )
         utilities.assert_equals( expect=main.TRUE,
                                  actual=mnStatus,
                                  onpass="Successfully started Mininet",
                                  onfail="Failed to activate Mininet" )
 
-        main.step("Assinging masters to switches")
+        main.step( "Assinging masters to switches" )
         switches = main.Mininet1.getSwitches()
-        swStatus = main.Mininet1.assignSwController( sw=switches.keys(), ip=main.ONOSip )
+        swStatus = main.Mininet1.assignSwController( sw=switches.keys(), ip=main.Cluster.getIps() )
         utilities.assert_equals( expect=main.TRUE,
                                  actual=swStatus,
                                  onpass="Successfully assigned switches to masters",
                                  onfail="Failed assign switches to masters" )
 
-        time.sleep(main.startUpSleep)
+        time.sleep( main.startUpSleep )
         # Balancing Masters
         main.step( "Balancing Masters" )
         stepResult = main.FALSE
-        stepResult = utilities.retry( main.CLIs[0].balanceMasters,
+        stepResult = utilities.retry( main.Cluster.active( 0 ).CLI.balanceMasters,
                                       main.FALSE,
                                       [],
                                       sleep=3,
@@ -244,11 +244,11 @@
                                        onpass="Balance masters was successfull",
                                        onfail="Failed to balance masters" )
 
-        main.log.info("Getting default flows")
-        jsonSum = json.loads(main.CLIs[0].summary())
-        main.defaultFlows = jsonSum["flows"]
+        main.log.info( "Getting default flows" )
+        jsonSum = json.loads( main.Cluster.active( 0 ).CLI.summary() )
+        main.defaultFlows = jsonSum[ "flows" ]
 
-        main.step("Check status of Mininet setup")
+        main.step( "Check status of Mininet setup" )
         caseResult = mnStatus and swStatus
         utilities.assert_equals( expect=main.TRUE,
                                  actual=caseResult,
@@ -258,11 +258,11 @@
         # This tells the following cases if we are using the null-provider or ovs
         main.switchType = "of:"
 
-        time.sleep(main.startUpSleep)
+        time.sleep( main.startUpSleep )
         main.step( "Balancing Masters" )
 
         stepResult = main.FALSE
-        stepResult = utilities.retry( main.CLIs[0].balanceMasters,
+        stepResult = utilities.retry( main.Cluster.active( 0 ).CLI.balanceMasters,
                                       main.FALSE,
                                       [],
                                       sleep=3,
@@ -271,9 +271,9 @@
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
                                  onpass="Balance masters was successfull",
-                                 onfail="Failed to balance masters")
+                                 onfail="Failed to balance masters" )
 
-        time.sleep(5)
+        time.sleep( 5 )
         if not caseResult:
             main.setupSkipped = True
 
@@ -290,15 +290,15 @@
         except ( NameError, AttributeError ):
             main.Utils = Utils()
         if main.reroute:
-            main.minIntents = int(main.params[ 'NULL' ][ 'REROUTE' ][ 'min_intents' ] )
-            main.maxIntents = int(main.params[ 'NULL' ][ 'REROUTE' ][ 'max_intents' ] )
-            main.checkInterval = int(main.params[ 'NULL' ][ 'REROUTE' ][ 'check_interval' ] )
-            main.batchSize = int(main.params[ 'NULL' ][ 'REROUTE' ][ 'batch_size' ] )
+            main.minIntents = int( main.params[ 'NULL' ][ 'REROUTE' ][ 'min_intents' ] )
+            main.maxIntents = int( main.params[ 'NULL' ][ 'REROUTE' ][ 'max_intents' ] )
+            main.checkInterval = int( main.params[ 'NULL' ][ 'REROUTE' ][ 'check_interval' ] )
+            main.batchSize = int( main.params[ 'NULL' ][ 'REROUTE' ][ 'batch_size' ] )
         else:
-            main.minIntents = int(main.params[ 'NULL' ][ 'PUSH' ][ 'min_intents' ] )
-            main.maxIntents = int(main.params[ 'NULL' ][ 'PUSH' ][ 'max_intents' ] )
-            main.checkInterval = int(main.params[ 'NULL' ][ 'PUSH' ][ 'check_interval' ] )
-            main.batchSize = int(main.params[ 'NULL' ][ 'PUSH' ][ 'batch_size' ] )
+            main.minIntents = int( main.params[ 'NULL' ][ 'PUSH' ][ 'min_intents' ] )
+            main.maxIntents = int( main.params[ 'NULL' ][ 'PUSH' ][ 'max_intents' ] )
+            main.checkInterval = int( main.params[ 'NULL' ][ 'PUSH' ][ 'check_interval' ] )
+            main.batchSize = int( main.params[ 'NULL' ][ 'PUSH' ][ 'batch_size' ] )
 
         # check if the case needs to be skipped
         if main.setupSkipped:
@@ -312,7 +312,7 @@
         # keeps track of how many flows have been installed, set to 0 at start
         currFlows = 0
         # limit for the number of intents that can be installed
-        main.batchSize = int( int(main.batchSize)/int(main.numCtrls))
+        main.batchSize = int( int( main.batchSize ) / main.Cluster.numCtrls )
         limit = main.maxIntents / main.batchSize
         # total intents installed
         totalIntents = 0
@@ -324,26 +324,26 @@
         stepResult = main.TRUE
         # temp variable to contain the number of flows
         flowsNum = 0
-        if main.numCtrls > 1:
+        if main.Cluster.numCtrls > 1:
             # if more than one onos nodes, we should check more frequently
-            main.checkInterval = main.checkInterval/4
+            main.checkInterval = main.checkInterval / 4
 
         # make sure the checkInterval divisible batchSize
         main.checkInterval = int( int( main.checkInterval / main.batchSize ) * main.batchSize )
         flowTemp=0
         intentVerifyTemp = 0
         totalFlows=0
-        for i in range(limit):
+        for i in range( limit ):
 
             # Threads pool
             pool = []
 
-            for j in range( int( main.numCtrls) ):
-                if main.numCtrls > 1:
+            for j in range( main.Cluster.numCtrls ):
+                if main.Cluster.numCtrls > 1:
                     time.sleep( 1 )
                 offtmp = offfset + main.maxIntents * j
                 # Push intents by using threads
-                t = main.Thread( target=main.CLIs[j].pushTestIntents,
+                t = main.Thread( target=main.Cluster.active( j ).CLI.pushTestIntents,
                                  threadID=main.threadID,
                                  name="Push-Test-Intents",
                                  args=[ main.switchType + main.ingress,
@@ -353,8 +353,8 @@
                                           "options": "-i",
                                           "timeout": main.timeout,
                                           "background":False,
-                                          "noExit":True} )
-                pool.append(t)
+                                          "noExit":True } )
+                pool.append( t )
                 t.start()
                 main.threadID = main.threadID + 1
             for t in pool:
@@ -362,41 +362,41 @@
                 stepResult = stepResult and t.result
             offfset = offfset + main.batchSize
 
-            totalIntents = main.batchSize * main.numCtrls + totalIntents
+            totalIntents = main.batchSize * main.Cluster.numCtrls + totalIntents
             if totalIntents >= main.minIntents and totalIntents % main.checkInterval == 0:
                 # if reach to minimum number and check interval, verify Intetns and flows
-                time.sleep( main.verifySleep * main.numCtrls )
+                time.sleep( main.verifySleep * main.Cluster.numCtrls )
 
-                main.log.info("Verify Intents states")
+                main.log.info( "Verify Intents states" )
                 # k is a control variable for verify retry attempts
                 k = 1
                 while k <= main.verifyAttempts:
                     # while loop for check intents by using CLI driver
-                    time.sleep(5)
-                    intentsState = main.CLIs[0].checkIntentSummary(timeout=600, noExit=True)
+                    time.sleep( 5 )
+                    intentsState = main.Cluster.active( 0 ).CLI.checkIntentSummary( timeout=600, noExit=True )
                     if intentsState:
-                        verifyTotalIntents = main.CLIs[0].getTotalIntentsNum(timeout=600, noExit=True)
+                        verifyTotalIntents = main.Cluster.active( 0 ).CLI.getTotalIntentsNum( timeout=600, noExit=True )
                         if intentVerifyTemp < verifyTotalIntents:
                             intentVerifyTemp = verifyTotalIntents
                         else:
                             verifyTotalIntents = intentVerifyTemp
                             intentsState = False
-                        main.log.info("Total Installed Intents: {}".format( verifyTotalIntents ) )
+                        main.log.info( "Total Installed Intents: {}".format( verifyTotalIntents ) )
                         break
-                    k = k+1
+                    k = k + 1
 
                 k = 1
                 flowVerify = True
                 while k <= main.verifyAttempts:
-                    time.sleep(5)
-                    totalFlows = main.CLIs[0].getTotalFlowsNum( timeout=600, noExit=True )
+                    time.sleep( 5 )
+                    totalFlows = main.Cluster.active( 0 ).CLI.getTotalFlowsNum( timeout=600, noExit=True )
                     expectFlows = totalIntents * 7 + main.defaultFlows
                     if totalFlows == expectFlows:
-                        main.log.info("Total Flows Added: {}".format(totalFlows))
+                        main.log.info( "Total Flows Added: {}".format( totalFlows ) )
                         break
                     else:
-                        main.log.info("Some Flows are not added, retry...")
-                        main.log.info("Total Flows Added: {} Expect Flows: {}".format(totalFlows, expectFlows))
+                        main.log.info( "Some Flows are not added, retry..." )
+                        main.log.info( "Total Flows Added: {} Expect Flows: {}".format( totalFlows, expectFlows ) )
                         flowVerify = False
 
                     k += 1
@@ -408,7 +408,7 @@
                 if not intentsState or not flowVerify:
                     # If some intents are not installed, grep the previous flows list, and finished this test case
                     main.log.warn( "Intents or flows are not installed" )
-                    verifyTotalIntents = main.CLIs[0].getTotalIntentsNum(timeout=600, noExit=True)
+                    verifyTotalIntents = main.Cluster.active( 0 ).CLI.getTotalIntentsNum( timeout=600, noExit=True )
                     if intentVerifyTemp < verifyTotalIntents:
                         intentVerifyTemp = verifyTotalIntents
                     else:
@@ -417,7 +417,7 @@
                         flowTemp = totalFlows
                     else:
                         totalFlows = flowTemp
-                    main.log.info("Total Intents: {}".format( verifyTotalIntents) )
+                    main.log.info( "Total Intents: {}".format( verifyTotalIntents ) )
                     break
 
         utilities.assert_equals( expect = main.TRUE,
@@ -430,17 +430,17 @@
 
         main.Utils.mininetCleanup( main.Mininet1 )
 
-        main.log.info("Writing results to DS file")
-        with open(main.dbFileName, "a") as dbFile:
+        main.log.info( "Writing results to DS file" )
+        with open( main.dbFileName, "a" ) as dbFile:
             # Scale number
             temp = "'" + main.commit + "',"
             temp += "'" + main.nic + "',"
-            temp += str(main.numCtrls)
+            temp += str( main.Cluster.numCtrls )
             temp += ",'" + "baremetal1" + "'"
             # how many intents we installed before crash
-            temp += "," + str(verifyTotalIntents)
+            temp += "," + str( verifyTotalIntents )
             # how many flows we installed before crash
-            temp += "," + str(totalFlows)
+            temp += "," + str( totalFlows )
             # other columns in database, but we didn't use in this test
             temp += "," + "0,0,0,0,0,0"
             temp += "\n"
diff --git a/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.topo b/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.topo
index cb54fe8..8b074c7 100755
--- a/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.topo
+++ b/TestON/tests/SCPF/SCPFscalingMaxIntents/SCPFscalingMaxIntents.topo
@@ -1,120 +1,36 @@
 <TOPOLOGY>
-
     <COMPONENT>
 
-        <ONOSbench>
-            <host>localhost</host>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
             <user>sdn</user>
             <password>rocks</password>
-            <type>OnosDriver</type>
+            <type>OnosClusterDriver</type>
             <connect_order>1</connect_order>
             <COMPONENTS>
-                <home>~/onos</home>
-                <nodes>7</nodes>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOSbench>
-
-        <ONOSrest1>
-            <host>OC1</host>
-            <port>8181</port>
-            <user>onos</user>
-            <password>rocks</password>
-            <type>OnosRestDriver</type>
-            <connect_order>3</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOSrest1>
-        <ONOScli1>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>2</connect_order>
-            <COMPONENTS>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost></diff_clihost>  # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
                 <karaf_username></karaf_username>
                 <karaf_password></karaf_password>
-                <prompt></prompt>
+                <web_user></web_user>
+                <web_pass></web_pass>
+                <rest_port></rest_port>
+                <prompt></prompt>  # TODO: we technically need a few of these, one per component
+                <onos_home></onos_home>  # defines where onos home is
+                <nodes> 7 </nodes>  # number of nodes in the cluster
             </COMPONENTS>
-        </ONOScli1>
-
-        <ONOScli2>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>3</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli2>
-
-        <ONOScli3>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>4</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli3>
-
-        <ONOScli4>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>5</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli4>
-
-        <ONOScli5>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>6</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli5>
-
-        <ONOScli6>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>7</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli6>
-
-        <ONOScli7>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>8</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli7>
+        </ONOScell>
 
         <Mininet1>
             <host>OCN</host>
             <user>sdn</user>
             <password>rocks</password>
             <type>MininetCliDriver</type>
-            <connect_order>5</connect_order>
+            <connect_order>2</connect_order>
             <COMPONENTS>
                 <prompt></prompt>
             </COMPONENTS>
         </Mininet1>
 
     </COMPONENT>
-</TOPOLOGY>
+</TOPOLOGY>
\ No newline at end of file
diff --git a/TestON/tests/SCPF/SCPFswitchLat/SCPFswitchLat.params b/TestON/tests/SCPF/SCPFswitchLat/SCPFswitchLat.params
index f06a7c8..d77eec3 100644
--- a/TestON/tests/SCPF/SCPFswitchLat/SCPFswitchLat.params
+++ b/TestON/tests/SCPF/SCPFswitchLat/SCPFswitchLat.params
@@ -56,7 +56,9 @@
             <ACK>openflow\ \[ACK\]</ACK>
         </down>
     </TSHARK>
-
+    <CFG>
+        <defaultTopo>org.onosproject.net.topology.impl.DefaultTopologyProvider</defaultTopo>
+    </CFG>
     <TEST>
         #'on' or 'off' debug mode.
         #If on, logging will be more verbose and
diff --git a/TestON/tests/SCPF/SCPFswitchLat/SCPFswitchLat.py b/TestON/tests/SCPF/SCPFswitchLat/SCPFswitchLat.py
index 2a34010..9c75ef5 100644
--- a/TestON/tests/SCPF/SCPFswitchLat/SCPFswitchLat.py
+++ b/TestON/tests/SCPF/SCPFswitchLat/SCPFswitchLat.py
@@ -31,7 +31,7 @@
 
 class SCPFswitchLat:
 
-    def __init__(self):
+    def __init__( self ):
         self.default = ''
 
     def CASE0( self, main ):
@@ -58,46 +58,46 @@
             # The dictionary to record different type of wrongs
             main.wrong = { 'totalWrong': 0, 'skipDown' : 0, 'TsharkValueIncorrect': 0,
                     'TypeError' : 0, 'decodeJasonError': 0,
-                    'checkResultIncorrect': 0}
-            main.maxWrong = int( main.params['TEST'] ['MaxWrong'] )
-            main.resultRange = main.params['TEST']['ResultRange']
-            main.searchTerm = main.params['TEST']['SearchTerm']
-            main.MN1Ip = main.params['MN']['ip1']
+                    'checkResultIncorrect': 0 }
+            main.maxWrong = int( main.params[ 'TEST' ] [ 'MaxWrong' ] )
+            main.resultRange = main.params[ 'TEST' ][ 'ResultRange' ]
+            main.searchTerm = main.params[ 'TEST' ][ 'SearchTerm' ]
+            main.MN1Ip = main.params[ 'MN' ][ 'ip1' ]
             main.dependencyPath = main.testOnDirectory + \
-                                  main.params['DEPENDENCY']['path']
-            main.topoName = main.params['DEPENDENCY']['topology']
-            main.dependencyFunc = main.params['DEPENDENCY']['function']
-            main.cellName = main.params['ENV']['cellName']
-            main.apps = main.params['ENV']['cellApps']
-            main.scale = (main.params['SCALE']).split(",")
+                                  main.params[ 'DEPENDENCY' ][ 'path' ]
+            main.topoName = main.params[ 'DEPENDENCY' ][ 'topology' ]
+            main.dependencyFunc = main.params[ 'DEPENDENCY' ][ 'function' ]
+            main.cellName = main.params[ 'ENV' ][ 'cellName' ]
+            main.apps = main.params[ 'ENV' ][ 'cellApps' ]
+            main.scale = ( main.params[ 'SCALE' ] ).split( "," )
 
-            main.ofPackage = main.params['TSHARK']
-
-            main.tsharkResultPath = main.params['TEST']['tsharkResultPath']
-            main.sampleSize = int(main.params['TEST']['sampleSize'])
-            main.warmUp = int(main.params['TEST']['warmUp'])
-            main.dbFileName = main.params['DATABASE']['dbName']
-            main.startUpSleep = int(main.params['SLEEP']['startup'])
-            main.measurementSleep = int( main.params['SLEEP']['measure'] )
-            main.deleteSwSleep = int( main.params['SLEEP']['deleteSW'] )
-            main.maxScale = int( main.params['max'] )
-            main.timeout = int( main.params['TIMEOUT']['timeout'] )
-            main.MNSleep = int( main.params['SLEEP']['mininet'])
-            main.device = main.params['TEST']['device']
-            stepResult = main.testSetUp.gitPulling()
-            main.log.info("Create Database file " + main.dbFileName)
-            resultsDB = open(main.dbFileName, "w+")
+            main.ofPackage = main.params[ 'TSHARK' ]
+            main.defaultTopoCfg = main.params [ 'CFG' ][ 'defaultTopo' ]
+            main.tsharkResultPath = main.params[ 'TEST' ][ 'tsharkResultPath' ]
+            main.sampleSize = int( main.params[ 'TEST' ][ 'sampleSize' ] )
+            main.warmUp = int( main.params[ 'TEST' ][ 'warmUp' ] )
+            main.dbFileName = main.params[ 'DATABASE' ][ 'dbName' ]
+            main.startUpSleep = int( main.params[ 'SLEEP' ][ 'startup' ] )
+            main.measurementSleep = int( main.params[ 'SLEEP' ][ 'measure' ] )
+            main.deleteSwSleep = int( main.params[ 'SLEEP' ][ 'deleteSW' ] )
+            main.maxScale = int( main.params[ 'max' ] )
+            main.timeout = int( main.params[ 'TIMEOUT' ][ 'timeout' ] )
+            main.MNSleep = int( main.params[ 'SLEEP' ][ 'mininet' ] )
+            main.device = main.params[ 'TEST' ][ 'device' ]
+            stepResult = main.testSetUp.envSetup()
+            main.log.info( "Create Database file " + main.dbFileName )
+            resultsDB = open( main.dbFileName, "w+" )
             resultsDB.close()
 
-            main.switchFunc = imp.load_source(main.dependencyFunc,
+            main.switchFunc = imp.load_source( main.dependencyFunc,
                                            main.dependencyPath +
                                            main.dependencyFunc +
-                                           ".py")
+                                           ".py" )
         except Exception as e:
             main.testSetUp.envSetupException( e )
         main.testSetUp.evnSetupConclusion( stepResult )
         main.commit = main.commit.split( " " )[ 1 ]
-    def CASE1(self, main):
+    def CASE1( self, main ):
         # Clean up test environment and set up
         import time
         try:
@@ -110,198 +110,196 @@
         except ( NameError, AttributeError ):
             main.Utils = Utils()
         main.maxNumBatch = 0
-        main.testSetUp.getNumCtrls( True )
-        main.testSetUp.envSetup( includeGitPull=False, makeMaxNodes=False )
-        main.testSetUp.ONOSSetUp( main.Mininet1, True,
-                                  cellName=main.cellName, killRemoveMax=False,
-                                  CtrlsSet=False )
+        main.testSetUp.ONOSSetUp( main.Mininet1, main.Cluster, True,
+                                  cellName=main.cellName, killRemoveMax=False )
 
-        main.log.info("Configure apps")
-        main.CLIs[0].setCfg("org.onosproject.net.topology.impl.DefaultTopologyProvider",
-                            "maxEvents 1")
-        main.CLIs[0].setCfg("org.onosproject.net.topology.impl.DefaultTopologyProvider",
-                            "maxBatchMs 0")
-        main.CLIs[0].setCfg("org.onosproject.net.topology.impl.DefaultTopologyProvider",
-                            "maxIdleMs 0")
-        for i in range(main.numCtrls):
-            main.CLIs[i].logSet( "DEBUG", "org.onosproject.metrics.topology")
-        time.sleep(1)
+        main.log.info( "Configure apps" )
+        main.Cluster.active( 0 ).CLI.setCfg( main.defaultTopoCfg,
+                                             "maxEvents 1" )
+        main.Cluster.active( 0 ).CLI.setCfg( main.defaultTopoCfg,
+                                             "maxBatchMs 0" )
+        main.Cluster.active( 0 ).CLI.setCfg( main.defaultTopoCfg,
+                                             "maxIdleMs 0" )
+        main.Cluster.command( "logSet",
+                              args=[ "DEBUG", "org.onosproject.metrics.topology" ],
+                              specificDriver=2 )
+        time.sleep( 1 )
 
-        main.log.info("Copy topology file to Mininet")
-        main.ONOSbench.copyMininetFile(main.topoName,
-                                       main.dependencyPath,
-                                       main.Mininet1.user_name,
-                                       main.Mininet1.ip_address)
+        main.log.info( "Copy topology file to Mininet" )
+        main.ONOSbench.copyMininetFile( main.topoName,
+                                        main.dependencyPath,
+                                        main.Mininet1.user_name,
+                                        main.Mininet1.ip_address )
         main.Utils.mininetCleanup( main.Mininet1 )
-        time.sleep(main.MNSleep)
-        main.log.info("Start new mininet topology")
+        time.sleep( main.MNSleep )
+        main.log.info( "Start new mininet topology" )
         main.Mininet1.startNet()
-        main.log.info("Assign switch to controller to ONOS node 1")
+        main.log.info( "Assign switch to controller to ONOS node 1" )
 
-        time.sleep(2)
+        time.sleep( 2 )
 
-    def CASE2(self,main):
+    def CASE2( self, main ):
         import time
         import json
         import numpy
 
-        resultDict = {'up' : {}, 'down' : {}}
-        for i in range(1, main.numCtrls + 1):
-            resultDict['up'][ 'node' + str(i) ] = {}
-            resultDict['up'][ 'node' + str(i) ][ 'Ave' ] = {}
-            resultDict['up'][ 'node' + str(i) ][ 'Std' ] = {}
-            resultDict['up'][ 'node' + str(i) ][ 'T_F' ] = []#TCP to Feature
-            resultDict['up'][ 'node' + str(i) ][ 'F_R' ] = []#Feature to Role
-            resultDict['up'][ 'node' + str(i) ][ 'RQ_RR' ] = []#role request to role reply
-            resultDict['up'][ 'node' + str(i) ][ 'RR_D' ] = []#role reply to Device
-            resultDict['up'][ 'node' + str(i) ][ 'D_G' ] = []#Device to Graph
-            resultDict['up'][ 'node' + str(i) ][ 'E_E' ] = []#TCP to Graph
+        resultDict = { 'up' : {}, 'down' : {} }
+        for i in range( 1, main.Cluster.numCtrls + 1 ):
+            resultDict[ 'up' ][ 'node' + str( i ) ] = {}
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Ave' ] = {}
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Std' ] = {}
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'T_F' ] = []#TCP to Feature
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'F_R' ] = []#Feature to Role
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'RQ_RR' ] = []#role request to role reply
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'RR_D' ] = []#role reply to Device
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'D_G' ] = []#Device to Graph
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'E_E' ] = []#TCP to Graph
 
-        for i in range(1,main.numCtrls + 1):
-            resultDict['down'][ 'node' + str(i) ] = {}
-            resultDict['down'][ 'node' + str(i) ][ 'Ave' ] = {}
-            resultDict['down'][ 'node' + str(i) ][ 'Std' ] = {}
-            resultDict['down'][ 'node' + str(i) ][ 'FA_A' ] = []#Fin_ack to ACK
-            resultDict['down'][ 'node' + str(i) ][ 'A_D' ] = []#Ack to Device
-            resultDict['down'][ 'node' + str(i) ][ 'D_G' ] = []#Device to Graph
-            resultDict['down'][ 'node' + str(i) ][ 'E_E' ] = []#fin_ack to Graph
-        for i in range(1 , main.sampleSize + main.warmUp):
-            main.log.info("************************************************************")
-            main.log.info("************************ Iteration: {} **********************" .format(str( i )) )
+        for i in range( 1, main.Cluster.numCtrls + 1 ):
+            resultDict[ 'down' ][ 'node' + str( i ) ] = {}
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'Ave' ] = {}
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'Std' ] = {}
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'FA_A' ] = []#Fin_ack to ACK
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'A_D' ] = []#Ack to Device
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'D_G' ] = []#Device to Graph
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'E_E' ] = []#fin_ack to Graph
+        for i in range( 1 , main.sampleSize + main.warmUp ):
+            main.log.info( "************************************************************" )
+            main.log.info( "************************ Iteration: {} **********************" .format( str( i ) ) )
             if i < main.warmUp:
                 main.switchFunc.captureOfPack( main, main.device, main.ofPackage,
                                                "up", resultDict, True )
                 main.switchFunc.captureOfPack( main, main.device, main.ofPackage,
                                                "down", resultDict, True )
-                main.CLIs[0].removeDevice( "of:0000000000000001" )
+                main.Cluster.active( 0 ).CLI.removeDevice( "of:0000000000000001" )
             else:
                 main.switchFunc.captureOfPack( main, main.device, main.ofPackage,
                                                "up", resultDict, False )
-                main.switchFunc.captureOfPack (main, main.device, main.ofPackage,
+                main.switchFunc.captureOfPack ( main, main.device, main.ofPackage,
                                                "down", resultDict, False )
-                main.CLIs[0].removeDevice( "of:0000000000000001" )
+                main.Cluster.active( 0 ).CLI.removeDevice( "of:0000000000000001" )
 
         # Dictionary for result
         maxDict  = {}
-        maxDict['down'] = {}
-        maxDict['up'] = {}
-        maxDict['down']['max'] = 0
-        maxDict['up']['max'] = 0
-        maxDict['down']['node'] = 0
-        maxDict['up']['node'] = 0
+        maxDict[ 'down' ] = {}
+        maxDict[ 'up' ] = {}
+        maxDict[ 'down' ][ 'max' ] = 0
+        maxDict[ 'up' ][ 'max' ] = 0
+        maxDict[ 'down' ][ 'node' ] = 0
+        maxDict[ 'up' ][ 'node' ] = 0
 
-        for i in range(1, main.numCtrls + 1):
+        for i in range( 1, main.Cluster.numCtrls + 1 ):
             # calculate average and std for result, and grep the max End to End data
-            EtoEtemp = numpy.average( resultDict['up'][ 'node' + str(i) ]['E_E'] )
-            resultDict['up'][ 'node' + str(i) ][ 'Ave' ][ 'E_E' ] = EtoEtemp
-            if maxDict['up']['max'] < EtoEtemp:
+            EtoEtemp = numpy.average( resultDict[ 'up' ][ 'node' + str( i ) ][ 'E_E' ] )
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Ave' ][ 'E_E' ] = EtoEtemp
+            if maxDict[ 'up' ][ 'max' ] < EtoEtemp:
                 # get max End to End latency
-                maxDict['up']['max'] = EtoEtemp
-                maxDict['up']['node'] = i
-            resultDict['up']['node' + str(i)]['Ave']['T_F'] = numpy.average(resultDict['up']['node' + str(i)]['T_F'])
-            resultDict['up']['node' + str(i)]['Ave']['F_R'] = numpy.average(resultDict['up']['node' + str(i)]['F_R'])
-            resultDict['up']['node' + str(i)]['Ave']['RQ_RR'] = numpy.average(resultDict['up']['node' + str(i)]['RQ_RR'])
-            resultDict['up']['node' + str(i)]['Ave']['RR_D'] = numpy.average(resultDict['up']['node' + str(i)]['RR_D'])
-            resultDict['up']['node' + str(i)]['Ave']['D_G'] = numpy.average(resultDict['up']['node' + str(i)]['D_G'])
+                maxDict[ 'up' ][ 'max' ] = EtoEtemp
+                maxDict[ 'up' ][ 'node' ] = i
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Ave' ][ 'T_F' ] = numpy.average( resultDict[ 'up' ][ 'node' + str( i ) ][ 'T_F' ] )
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Ave' ][ 'F_R' ] = numpy.average( resultDict[ 'up' ][ 'node' + str( i ) ][ 'F_R' ] )
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Ave' ][ 'RQ_RR' ] = numpy.average( resultDict[ 'up' ][ 'node' + str( i ) ][ 'RQ_RR' ] )
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Ave' ][ 'RR_D' ] = numpy.average( resultDict[ 'up' ][ 'node' + str( i ) ][ 'RR_D' ] )
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Ave' ][ 'D_G' ] = numpy.average( resultDict[ 'up' ][ 'node' + str( i ) ][ 'D_G' ] )
 
-            resultDict['up'][ 'node' + str(i) ][ 'Std' ][ 'E_E' ] = numpy.std( resultDict['up'][ 'node' + str(i) ]['E_E'] )
-            resultDict['up']['node' + str(i)]['Std']['T_F'] = numpy.std(resultDict['up']['node' + str(i)]['T_F'])
-            resultDict['up']['node' + str(i)]['Std']['F_R'] = numpy.std(resultDict['up']['node' + str(i)]['F_R'])
-            resultDict['up']['node' + str(i)]['Std']['RQ_RR'] = numpy.std(resultDict['up']['node' + str(i)]['RQ_RR'])
-            resultDict['up']['node' + str(i)]['Std']['RR_D'] = numpy.std(resultDict['up']['node' + str(i)]['RR_D'])
-            resultDict['up']['node' + str(i)]['Std']['D_G'] = numpy.std(resultDict['up']['node' + str(i)]['D_G'])
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Std' ][ 'E_E' ] = numpy.std( resultDict[ 'up' ][ 'node' + str( i ) ][ 'E_E' ] )
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Std' ][ 'T_F' ] = numpy.std( resultDict[ 'up' ][ 'node' + str( i ) ][ 'T_F' ] )
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Std' ][ 'F_R' ] = numpy.std( resultDict[ 'up' ][ 'node' + str( i ) ][ 'F_R' ] )
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Std' ][ 'RQ_RR' ] = numpy.std( resultDict[ 'up' ][ 'node' + str( i ) ][ 'RQ_RR' ] )
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Std' ][ 'RR_D' ] = numpy.std( resultDict[ 'up' ][ 'node' + str( i ) ][ 'RR_D' ] )
+            resultDict[ 'up' ][ 'node' + str( i ) ][ 'Std' ][ 'D_G' ] = numpy.std( resultDict[ 'up' ][ 'node' + str( i ) ][ 'D_G' ] )
 
             # calculate average and std for result, and grep the max End to End data
-            EtoEtemp = numpy.average( resultDict['down'][ 'node' + str(i) ]['E_E'] )
-            resultDict['down'][ 'node' + str(i) ][ 'Ave' ][ 'E_E' ] = EtoEtemp
-            if maxDict['down']['max'] < EtoEtemp:
+            EtoEtemp = numpy.average( resultDict[ 'down' ][ 'node' + str( i ) ][ 'E_E' ] )
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'Ave' ][ 'E_E' ] = EtoEtemp
+            if maxDict[ 'down' ][ 'max' ] < EtoEtemp:
                 # get max End to End latency
-                maxDict['down']['max'] = EtoEtemp
-                maxDict['down']['node'] = i
-            resultDict['down']['node' + str(i)]['Ave']['FA_A'] = numpy.average(resultDict['down']['node' + str(i)]['FA_A'])
-            resultDict['down']['node' + str(i)]['Ave']['A_D'] = numpy.average(resultDict['down']['node' + str(i)]['A_D'])
-            resultDict['down']['node' + str(i)]['Ave']['D_G'] = numpy.average(resultDict['down']['node' + str(i)]['D_G'])
+                maxDict[ 'down' ][ 'max' ] = EtoEtemp
+                maxDict[ 'down' ][ 'node' ] = i
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'Ave' ][ 'FA_A' ] = numpy.average( resultDict[ 'down' ][ 'node' + str( i ) ][ 'FA_A' ] )
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'Ave' ][ 'A_D' ] = numpy.average( resultDict[ 'down' ][ 'node' + str( i ) ][ 'A_D' ] )
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'Ave' ][ 'D_G' ] = numpy.average( resultDict[ 'down' ][ 'node' + str( i ) ][ 'D_G' ] )
 
-            resultDict['down'][ 'node' + str(i) ][ 'Std' ][ 'E_E' ] = numpy.std( resultDict['down'][ 'node' + str(i) ]['E_E'] )
-            resultDict['down']['node' + str(i)]['Std']['FA_A'] = numpy.std(resultDict['down']['node' + str(i)]['FA_A'])
-            resultDict['down']['node' + str(i)]['Std']['A_D'] = numpy.std(resultDict['down']['node' + str(i)]['A_D'])
-            resultDict['down']['node' + str(i)]['Std']['D_G'] = numpy.std(resultDict['down']['node' + str(i)]['D_G'])
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'Std' ][ 'E_E' ] = numpy.std( resultDict[ 'down' ][ 'node' + str( i ) ][ 'E_E' ] )
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'Std' ][ 'FA_A' ] = numpy.std( resultDict[ 'down' ][ 'node' + str( i ) ][ 'FA_A' ] )
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'Std' ][ 'A_D' ] = numpy.std( resultDict[ 'down' ][ 'node' + str( i ) ][ 'A_D' ] )
+            resultDict[ 'down' ][ 'node' + str( i ) ][ 'Std' ][ 'D_G' ] = numpy.std( resultDict[ 'down' ][ 'node' + str( i ) ][ 'D_G' ] )
 
-            main.log.report( "=====node{} Summary:=====".format( str(i) ) )
+            main.log.report( "=====node{} Summary:=====".format( str( i ) ) )
             main.log.report( "=============Switch up=======" )
 
             main.log.report(
-                            "End to End average: {}".format( str(resultDict["up"][ 'node' + str(i) ][ 'Ave' ][ 'E_E' ]) ) )
+                            "End to End average: {}".format( str( resultDict[ "up" ][ 'node' + str( i ) ][ 'Ave' ][ 'E_E' ] ) ) )
             main.log.report(
-                            "End to End Std: {}".format( str(resultDict["up"][ 'node' + str(i) ][ 'Std' ][ 'E_E' ]) ) )
+                            "End to End Std: {}".format( str( resultDict[ "up" ][ 'node' + str( i ) ][ 'Std' ][ 'E_E' ] ) ) )
 
             main.log.report(
-                            "TCP to Feature average: {}".format( str(resultDict["up"][ 'node' + str(i) ][ 'Ave' ][ 'T_F' ]) ) )
+                            "TCP to Feature average: {}".format( str( resultDict[ "up" ][ 'node' + str( i ) ][ 'Ave' ][ 'T_F' ] ) ) )
             main.log.report(
-                            "TCP to Feature Std: {}".format( str(resultDict["up"][ 'node' + str(i) ][ 'Std' ][ 'T_F' ]) ) )
+                            "TCP to Feature Std: {}".format( str( resultDict[ "up" ][ 'node' + str( i ) ][ 'Std' ][ 'T_F' ] ) ) )
 
             main.log.report(
-                            "Feature to Role average: {}".format( str(resultDict["up"][ 'node' + str(i) ][ 'Ave' ][ 'F_R' ]) ) )
+                            "Feature to Role average: {}".format( str( resultDict[ "up" ][ 'node' + str( i ) ][ 'Ave' ][ 'F_R' ] ) ) )
             main.log.report(
-                            "Feature to Role Std: {}".format( str(resultDict["up"][ 'node' + str(i) ][ 'Std' ][ 'F_R' ]) ) )
+                            "Feature to Role Std: {}".format( str( resultDict[ "up" ][ 'node' + str( i ) ][ 'Std' ][ 'F_R' ] ) ) )
 
             main.log.report(
-                            "Role request to Role reply average: {}".format( str(resultDict["up"][ 'node' + str(i) ][ 'Ave' ][ 'RQ_RR' ]) ) )
+                            "Role request to Role reply average: {}".format( str( resultDict[ "up" ][ 'node' + str( i ) ][ 'Ave' ][ 'RQ_RR' ] ) ) )
             main.log.report(
-                            "Role request to Role reply Std: {}".format( str(resultDict["up"][ 'node' + str(i) ][ 'Std' ][ 'RQ_RR' ]) ) )
+                            "Role request to Role reply Std: {}".format( str( resultDict[ "up" ][ 'node' + str( i ) ][ 'Std' ][ 'RQ_RR' ] ) ) )
 
             main.log.report(
-                            "Role reply to Device average: {}".format( str(resultDict["up"][ 'node' + str(i) ][ 'Ave' ][ 'RR_D' ]) ) )
+                            "Role reply to Device average: {}".format( str( resultDict[ "up" ][ 'node' + str( i ) ][ 'Ave' ][ 'RR_D' ] ) ) )
             main.log.report(
-                            "Role reply to Device Std: {}".format( str(resultDict["up"][ 'node' + str(i) ][ 'Std' ][ 'RR_D' ]) ) )
+                            "Role reply to Device Std: {}".format( str( resultDict[ "up" ][ 'node' + str( i ) ][ 'Std' ][ 'RR_D' ] ) ) )
 
             main.log.report(
-                            "Device to Graph average: {}".format( str(resultDict["up"][ 'node' + str(i) ][ 'Ave' ][ 'D_G' ]) ) )
+                            "Device to Graph average: {}".format( str( resultDict[ "up" ][ 'node' + str( i ) ][ 'Ave' ][ 'D_G' ] ) ) )
             main.log.report(
-                            "Device to Graph Std: {}".format( str(resultDict["up"][ 'node' + str(i) ][ 'Std' ][ 'D_G' ]) ) )
+                            "Device to Graph Std: {}".format( str( resultDict[ "up" ][ 'node' + str( i ) ][ 'Std' ][ 'D_G' ] ) ) )
 
             main.log.report( "=============Switch down=======" )
 
             main.log.report(
-                            "End to End average: {}".format( str(resultDict["down"][ 'node' + str(i) ][ 'Ave' ][ 'E_E' ]) ) )
+                            "End to End average: {}".format( str( resultDict[ "down" ][ 'node' + str( i ) ][ 'Ave' ][ 'E_E' ] ) ) )
             main.log.report(
-                            "End to End Std: {}".format( str(resultDict["down"][ 'node' + str(i) ][ 'Std' ][ 'E_E' ]) ) )
+                            "End to End Std: {}".format( str( resultDict[ "down" ][ 'node' + str( i ) ][ 'Std' ][ 'E_E' ] ) ) )
 
             main.log.report(
-                            "Fin_ACK to ACK average: {}".format( str(resultDict["down"][ 'node' + str(i) ][ 'Ave' ][ 'FA_A' ]) ) )
+                            "Fin_ACK to ACK average: {}".format( str( resultDict[ "down" ][ 'node' + str( i ) ][ 'Ave' ][ 'FA_A' ] ) ) )
             main.log.report(
-                            "Fin_ACK to ACK Std: {}".format( str(resultDict["down"][ 'node' + str(i) ][ 'Std' ][ 'FA_A' ]) ) )
+                            "Fin_ACK to ACK Std: {}".format( str( resultDict[ "down" ][ 'node' + str( i ) ][ 'Std' ][ 'FA_A' ] ) ) )
 
             main.log.report(
-                            "ACK to Device average: {}".format( str(resultDict["down"][ 'node' + str(i) ][ 'Ave' ][ 'A_D' ]) ) )
+                            "ACK to Device average: {}".format( str( resultDict[ "down" ][ 'node' + str( i ) ][ 'Ave' ][ 'A_D' ] ) ) )
             main.log.report(
-                            "ACK to Device Std: {}".format( str(resultDict["down"][ 'node' + str(i) ][ 'Std' ][ 'A_D' ]) ) )
+                            "ACK to Device Std: {}".format( str( resultDict[ "down" ][ 'node' + str( i ) ][ 'Std' ][ 'A_D' ] ) ) )
 
             main.log.report(
-                            "Device to Graph average: {}".format( str(resultDict["down"][ 'node' + str(i) ][ 'Ave' ][ 'D_G' ]) ) )
+                            "Device to Graph average: {}".format( str( resultDict[ "down" ][ 'node' + str( i ) ][ 'Ave' ][ 'D_G' ] ) ) )
             main.log.report(
-                            "Device to Graph Std: {}".format( str(resultDict["down"][ 'node' + str(i) ][ 'Std' ][ 'D_G' ]) ) )
+                            "Device to Graph Std: {}".format( str( resultDict[ "down" ][ 'node' + str( i ) ][ 'Std' ][ 'D_G' ] ) ) )
 
-        with open(main.dbFileName, "a") as dbFile:
+        with open( main.dbFileName, "a" ) as dbFile:
             # TODO: Save STD to Database
             # Scale number
-            temp = str(main.numCtrls)
+            temp = str( main.Cluster.numCtrls )
             temp += ",'baremetal1'"
             # put result
-            temp += "," + str( "%.2f" % resultDict['up'][ 'node' + str(maxDict['up']['node']) ][ 'Ave' ][ 'E_E' ] )
-            temp += "," + str( "%.2f" % resultDict['up'][ 'node' + str(maxDict['up']['node']) ][ 'Ave' ][ 'T_F' ] )
-            temp += "," + str( "%.2f" % resultDict['up'][ 'node' + str(maxDict['up']['node']) ][ 'Ave' ][ 'F_R' ] )
-            temp += "," + str( "%.2f" % resultDict['up'][ 'node' + str(maxDict['up']['node']) ][ 'Ave' ][ 'RQ_RR' ] )
-            temp += "," + str( "%.2f" % resultDict['up'][ 'node' + str(maxDict['up']['node']) ][ 'Ave' ][ 'RR_D' ] )
-            temp += "," + str( "%.2f" % resultDict['up'][ 'node' + str(maxDict['up']['node']) ][ 'Ave' ][ 'D_G' ] )
+            temp += "," + str( "%.2f" % resultDict[ 'up' ][ 'node' + str( maxDict[ 'up' ][ 'node' ] ) ][ 'Ave' ][ 'E_E' ] )
+            temp += "," + str( "%.2f" % resultDict[ 'up' ][ 'node' + str( maxDict[ 'up' ][ 'node' ] ) ][ 'Ave' ][ 'T_F' ] )
+            temp += "," + str( "%.2f" % resultDict[ 'up' ][ 'node' + str( maxDict[ 'up' ][ 'node' ] ) ][ 'Ave' ][ 'F_R' ] )
+            temp += "," + str( "%.2f" % resultDict[ 'up' ][ 'node' + str( maxDict[ 'up' ][ 'node' ] ) ][ 'Ave' ][ 'RQ_RR' ] )
+            temp += "," + str( "%.2f" % resultDict[ 'up' ][ 'node' + str( maxDict[ 'up' ][ 'node' ] ) ][ 'Ave' ][ 'RR_D' ] )
+            temp += "," + str( "%.2f" % resultDict[ 'up' ][ 'node' + str( maxDict[ 'up' ][ 'node' ] ) ][ 'Ave' ][ 'D_G' ] )
 
-            temp += "," + str( "%.2f" % resultDict['down'][ 'node' + str(maxDict['down']['node']) ][ 'Ave' ][ 'E_E' ] )
-            temp += "," + str( "%.2f" % resultDict['down'][ 'node' + str(maxDict['down']['node']) ][ 'Ave' ][ 'FA_A' ] )
-            temp += "," + str( "%.2f" % resultDict['down'][ 'node' + str(maxDict['down']['node']) ][ 'Ave' ][ 'A_D' ] )
-            temp += "," + str( "%.2f" % resultDict['down'][ 'node' + str(maxDict['down']['node']) ][ 'Ave' ][ 'D_G' ] )
+            temp += "," + str( "%.2f" % resultDict[ 'down' ][ 'node' + str( maxDict[ 'down' ][ 'node' ] ) ][ 'Ave' ][ 'E_E' ] )
+            temp += "," + str( "%.2f" % resultDict[ 'down' ][ 'node' + str( maxDict[ 'down' ][ 'node' ] ) ][ 'Ave' ][ 'FA_A' ] )
+            temp += "," + str( "%.2f" % resultDict[ 'down' ][ 'node' + str( maxDict[ 'down' ][ 'node' ] ) ][ 'Ave' ][ 'A_D' ] )
+            temp += "," + str( "%.2f" % resultDict[ 'down' ][ 'node' + str( maxDict[ 'down' ][ 'node' ] ) ][ 'Ave' ][ 'D_G' ] )
 
-            temp += "," + str( "%.2f" % resultDict['up'][ 'node' + str(maxDict['up']['node']) ][ 'Std' ][ 'E_E' ] )
-            temp += "," + str( "%.2f" % resultDict['down'][ 'node' + str(maxDict['down']['node']) ][ 'Std' ][ 'E_E' ] )
+            temp += "," + str( "%.2f" % resultDict[ 'up' ][ 'node' + str( maxDict[ 'up' ][ 'node' ] ) ][ 'Std' ][ 'E_E' ] )
+            temp += "," + str( "%.2f" % resultDict[ 'down' ][ 'node' + str( maxDict[ 'down' ][ 'node' ] ) ][ 'Std' ][ 'E_E' ] )
 
             temp += "\n"
             dbFile.write( temp )
diff --git a/TestON/tests/SCPF/SCPFswitchLat/SCPFswitchLat.topo b/TestON/tests/SCPF/SCPFswitchLat/SCPFswitchLat.topo
index 7b01b63..e4b8ee4 100644
--- a/TestON/tests/SCPF/SCPFswitchLat/SCPFswitchLat.topo
+++ b/TestON/tests/SCPF/SCPFswitchLat/SCPFswitchLat.topo
@@ -1,116 +1,32 @@
 <TOPOLOGY>
-
     <COMPONENT>
 
-        <ONOSbench>
-            <host>localhost</host>
+        <ONOScell>
+            <host>localhost</host>  # ONOS "bench" machine
             <user>sdn</user>
             <password>rocks</password>
-            <type>OnosDriver</type>
+            <type>OnosClusterDriver</type>
             <connect_order>1</connect_order>
             <COMPONENTS>
-                <home>~/onos</home>
-                <nodes>7</nodes>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOSbench>
-
-        <ONOScli1>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>2</connect_order>
-            <COMPONENTS>
+                <cluster_name></cluster_name>  # Used as a prefix for cluster components. Defaults to 'ONOS'
+                <diff_clihost></diff_clihost>  # if it has different host other than localhost for CLI. True or empty. OC# will be used if True.
                 <karaf_username></karaf_username>
                 <karaf_password></karaf_password>
-                <prompt></prompt>
+                <web_user></web_user>
+                <web_pass></web_pass>
+                <rest_port></rest_port>
+                <prompt></prompt>  # TODO: we technically need a few of these, one per component
+                <onos_home></onos_home>  # defines where onos home is
+                <nodes> 7 </nodes>  # number of nodes in the cluster
             </COMPONENTS>
-        </ONOScli1>
-
-        <ONOScli2>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>3</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli2>
-
-        <ONOScli3>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>4</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli3>
-
-        <ONOScli4>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>5</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli4>
-
-        <ONOScli5>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>6</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli5>
-
-        <ONOScli6>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>7</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli6>
-
-        <ONOScli7>
-            <host>localhost</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosCliDriver</type>
-            <connect_order>8</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOScli7>
-
-        <ONOS1>
-            <host>OC1</host>
-            <user>sdn</user>
-            <password>rocks</password>
-            <type>OnosDriver</type>
-            <connect_order>9</connect_order>
-            <COMPONENTS>
-                <prompt></prompt>
-            </COMPONENTS>
-        </ONOS1>
+        </ONOScell>
 
         <Mininet1>
             <host>OCN</host>
             <user>sdn</user>
             <password>rocks</password>
             <type>MininetCliDriver</type>
-            <connect_order>16</connect_order>
+            <connect_order>2</connect_order>
             <COMPONENTS>
                 <arg1> --custom ~/mininet/custom/topo-perf-1sw.py </arg1>
                 <arg2> --topo mytopo</arg2>
@@ -122,6 +38,4 @@
         </Mininet1>
 
     </COMPONENT>
-
-</TOPOLOGY>
- 
+</TOPOLOGY>
\ No newline at end of file
diff --git a/TestON/tests/SCPF/SCPFswitchLat/dependencies/switchFunc.py b/TestON/tests/SCPF/SCPFswitchLat/dependencies/switchFunc.py
index 5526f9d..829c846 100644
--- a/TestON/tests/SCPF/SCPFswitchLat/dependencies/switchFunc.py
+++ b/TestON/tests/SCPF/SCPFswitchLat/dependencies/switchFunc.py
@@ -36,7 +36,7 @@
         searchTerm: the key term of timestamp
 
     '''
-    lines = main.CLIs[ index ].logSearch( mode='last', searchTerm=searchTerm )
+    lines = main.Cluster.active( index ).CLI.logSearch( mode='last', searchTerm=searchTerm )
     try:
         assert lines != None
         logString = lines[ len ( lines ) - 1 ]
@@ -178,7 +178,7 @@
         # if up, assign switch to controller
         time.sleep( main.measurementSleep )
         main.log.info( 'Assigning {} to controller'.format( deviceName ))
-        main.Mininet1.assignSwController( sw=deviceName, ip=main.ONOSip[0] )
+        main.Mininet1.assignSwController( sw=deviceName, ip=main.Cluster.active( 0 ).ipAddress )
         time.sleep( main.measurementSleep )
     if switchStatus == 'down':
         # if down, remove switch from topology
@@ -272,14 +272,14 @@
             main.log.info( "{} Feature to Role Request: {}".format( d, str( F_Rtemp ) ) )
             main.log.info( "{} Role Request to Role Reply: {}".format( d, str( RQ_RRtemp ) ) )
 
-        for i in range( 1, main.numCtrls + 1 ):
+        for i in range( 1, main.Cluster.numCtrls + 1 ):
             RR_Dtemp = 0
             D_Gtemp = 0
             E_Etemp = 0
             main.log.info( "================================================" )
             # get onos metrics timestamps
             try:
-                response = json.loads( main.CLIs[i - 1].topologyEventsMetrics() )
+                response = json.loads( main.Cluster.active( i - 1 ).CLI.topologyEventsMetrics() )
                 DeviceTime = getTimestampFromLog( i - 1, searchTerm=main.searchTerm[switchStatus] )
                 main.log.info( "ONOS{} device Event timestamp: {}".format( i, "%.2f" % DeviceTime ) )
                 GraphTime = int( response.get( "topologyGraphEventTimestamp" ).get( "value" ) )
@@ -352,15 +352,15 @@
             if not warmup:
                 resultDict[ switchStatus ][ d ][ 'FA_A' ].append( FA_Atemp )
             main.log.info( "{} FIN/ACK TO ACK {}:".format( d, FA_Atemp ) )
-        for i in range( 1, main.numCtrls + 1 ):
+        for i in range( 1, main.Cluster.numCtrls + 1 ):
             A_Dtemp = 0
             D_Gtemp = 0
             E_Etemp = 0
             main.log.info( "================================================" )
             # get onos metrics timestamps
             try:
-                response = json.loads( main.CLIs[ i - 1 ].topologyEventsMetrics() )
-                DeviceTime = getTimestampFromLog( i - 1, searchTerm=main.searchTerm[switchStatus] )
+                response = json.loads( main.Cluster.active( i - 1 ).CLI.topologyEventsMetrics() )
+                DeviceTime = getTimestampFromLog( i - 1, searchTerm=main.searchTerm[ switchStatus ] )
                 main.log.info( "ONOS{} device Event timestamp: {}".format( i, DeviceTime ) )
                 GraphTime = int( response.get( "topologyGraphEventTimestamp" ).get( "value" ) )
                 main.log.info( "ONOS{} Graph Event timestamp: {}".format( i, GraphTime ) )