Merge "add SCPFbatchFlowResp test case"
diff --git a/TestON/drivers/common/api/controller/onosrestdriver.py b/TestON/drivers/common/api/controller/onosrestdriver.py
index 3ed6ab3..ebccce0 100644
--- a/TestON/drivers/common/api/controller/onosrestdriver.py
+++ b/TestON/drivers/common/api/controller/onosrestdriver.py
@@ -1480,3 +1480,254 @@
             main.log.exception( self.name + ": Uncaught exception!" )
             main.cleanup()
             main.exit()
+
+    def createFlowBatch( self,
+                      numSw = 1,
+                      swIndex = 1,
+                      batchSize = 1,
+                      batchIndex = 1,
+                      deviceIdpreFix = "of:",
+                      appId=0,
+                      deviceID="",
+                      ingressPort="",
+                      egressPort="",
+                      ethType="",
+                      ethSrc="",
+                      ethDst="",
+                      vlan="",
+                      ipProto="",
+                      ipSrc=(),
+                      ipDst=(),
+                      tcpSrc="",
+                      tcpDst="",
+                      udpDst="",
+                      udpSrc="",
+                      mpls="",
+                      ip="DEFAULT",
+                      port="DEFAULT",
+                      debug=False ):
+        """
+        Description:
+            Creates batches of MAC-rule flows for POST.
+            Predefined MAC: 2 MS Hex digit for iterating devices
+                        Next 5 Hex digit for iterating batch numbers
+                        Next 5 Hex digit for iterating flows within a batch
+        Required:
+            * deviceId: id of the device
+        Optional:
+            * ingressPort: port ingress device
+            * egressPort: port  of egress device
+            * ethType: specify ethType
+            * ethSrc: specify ethSrc ( i.e. src mac addr )
+            * ethDst: specify ethDst ( i.e. dst mac addr )
+            * ipProto: specify ip protocol
+            * ipSrc: specify ip source address with mask eg. ip#/24
+                as a tuple (type, ip#)
+            * ipDst: specify ip destination address eg. ip#/24
+                as a tuple (type, ip#)
+            * tcpSrc: specify tcp source port
+            * tcpDst: specify tcp destination port
+        Returns:
+            Returns main.TRUE for successful requests; Returns main.FALSE
+            if error on requests;
+            Returns None for exceptions
+        NOTE:
+            The ip and port option are for the requests input's ip and port
+            of the ONOS node
+        """
+        #from pprint import pprint
+
+        flowJsonList = []
+        flowJsonBatch = {"flows":flowJsonList}
+        dev = swIndex
+
+        for fl in range(1, batchSize + 1):
+            flowJson = { "priority":100,
+                           "deviceId":"",
+                           "isPermanent":"true",
+                           "timeout":0,
+                           "treatment":{"instructions":[]},
+                           "selector": {"criteria":[]}}
+
+            #main.log.info("fl: " + str(fl))
+            if dev <= numSw:
+                deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
+                #print deviceId
+                flowJson['deviceId'] = deviceId
+                dev += 1
+            else:
+                dev = 1
+                deviceId = deviceIdpreFix + "{0:0{1}x}".format(dev,16)
+                #print deviceId
+                flowJson['deviceId'] = deviceId
+                dev += 1
+
+                # ethSrc starts with "0"; ethDst starts with "1"
+                # 2 Hex digit of device number; 5 digits of batch index number; 5 digits of batch size
+            ethS = "%02X" %int( "0" + "{0:0{1}b}".format(dev,7), 2 ) + \
+                   "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
+            ethSrc = ':'.join(ethS[i:i+2] for i in range(0,len(ethS),2))
+            ethD = "%02X" %int( "1" + "{0:0{1}b}".format(dev,7), 2 ) + \
+                   "{0:0{1}x}".format(batchIndex,5) + "{0:0{1}x}".format(fl,5)
+            ethDst = ':'.join(ethD[i:i+2] for i in range(0,len(ethD),2))
+
+            if appId:
+                flowJson[ "appId" ] = appId
+
+            if egressPort:
+                flowJson[ 'treatment' ][ 'instructions' ].append( {
+                                                        "type":"OUTPUT",
+                                                        "port":egressPort } )
+            if ingressPort:
+                flowJson[ 'selector' ][ 'criteria' ].append( {
+                                                        "type":"IN_PORT",
+                                                        "port":ingressPort } )
+            if ethType:
+                flowJson[ 'selector' ][ 'criteria' ].append( {
+                                                        "type":"ETH_TYPE",
+                                                        "ethType":ethType } )
+            if ethSrc:
+                flowJson[ 'selector' ][ 'criteria' ].append( {
+                                                        "type":"ETH_SRC",
+                                                        "mac":ethSrc } )
+            if ethDst:
+                flowJson[ 'selector' ][ 'criteria' ].append( {
+                                                        "type":"ETH_DST",
+                                                        "mac":ethDst } )
+            if vlan:
+                flowJson[ 'selector' ][ 'criteria' ].append( {
+                                                        "type":"VLAN_VID",
+                                                        "vlanId":vlan } )
+            if mpls:
+                flowJson[ 'selector' ][ 'criteria' ].append( {
+                                                        "type":"MPLS_LABEL",
+                                                        "label":mpls } )
+            if ipSrc:
+                flowJson[ 'selector' ][ 'criteria' ].append( {
+                                                        "type":ipSrc[0],
+                                                        "ip":ipSrc[1] } )
+            if ipDst:
+                flowJson[ 'selector' ][ 'criteria' ].append( {
+                                                        "type":ipDst[0],
+                                                        "ip":ipDst[1] } )
+            if tcpSrc:
+                flowJson[ 'selector' ][ 'criteria' ].append( {
+                                                        "type":"TCP_SRC",
+                                                        "tcpPort": tcpSrc } )
+            if tcpDst:
+                flowJson[ 'selector' ][ 'criteria' ].append( {
+                                                        "type":"TCP_DST",
+                                                        "tcpPort": tcpDst } )
+            if udpSrc:
+                flowJson[ 'selector' ][ 'criteria' ].append( {
+                                                        "type":"UDP_SRC",
+                                                        "udpPort": udpSrc } )
+            if udpDst:
+                flowJson[ 'selector' ][ 'criteria' ].append( {
+                                                        "type":"UDP_DST",
+                                                        "udpPort": udpDst } )
+            if ipProto:
+                flowJson[ 'selector' ][ 'criteria' ].append( {
+                                                        "type":"IP_PROTO",
+                                                        "protocol": ipProto } )
+            #pprint(flowJson)
+            flowJsonList.append(flowJson)
+
+        main.log.info("Number of flows in batch: " + str( len(flowJsonList) ) )
+        flowJsonBatch['flows'] = flowJsonList
+        #pprint(flowJsonBatch)
+
+        return flowJsonBatch
+
+
+    def sendFlowBatch( self, batch={}, ip="DEFAULT", port="DEFAULT", debug=False ):
+        """
+        Description:
+            Sends a single flow batch through /flows REST API.
+        Required:
+            * The batch of flows
+        Returns:
+            Returns main.TRUE for successful requests; Returns main.FALSE
+            if error on requests;
+            Returns None for exceptions
+        NOTE:
+            The ip and port option are for the requests input's ip and port
+            of the ONOS node
+        """
+        import time
+
+        try:
+            if debug: main.log.debug( "Adding flow: " + self.pprint( batch ) )
+            output = None
+            if ip == "DEFAULT":
+                main.log.warn( "No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( "No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            url = "/flows/"
+            response = self.send( ip,
+                                  port,
+                                  method="POST",
+                                  url=url,
+                                  data=json.dumps( batch ) )
+            #main.log.info("Post response is: ", str(response[0]))
+            if response[0] == 200:
+                main.log.info( self.name + ": Successfully POST flow batch" )
+                return main.TRUE, response
+            else:
+                main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                return main.FALSE
+        except NotImplementedError as e:
+            raise e  # Inform the caller
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
+
+    def removeFlowBatch( self, batch={},
+                       ip="DEFAULT", port="DEFAULT" ):
+        """
+        Description:
+            Remove a batch of flows
+        Required:
+            flow batch
+        Return:
+            Returns main.TRUE if successfully deletes flows, otherwise
+            Returns main.FALSE, Returns None on error
+        """
+        try:
+            output = None
+            if ip == "DEFAULT":
+                main.log.warn( "No ip given, reverting to ip from topo file" )
+                ip = self.ip_address
+            if port == "DEFAULT":
+                main.log.warn( "No port given, reverting to port " +
+                               "from topo file" )
+                port = self.port
+            # NOTE: REST url requires the intent id to be in decimal form
+
+            response = self.send( ip,
+                                  port,
+                                  method="DELETE",
+                                  url="/flows/",
+                                  data = json.dumps(batch) )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    return main.TRUE
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+        except ( AttributeError, TypeError ):
+            main.log.exception( self.name + ": Object not as expected" )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
diff --git a/TestON/tests/SCPFbatchFlowResp/SCPFbatchFlowResp.params b/TestON/tests/SCPFbatchFlowResp/SCPFbatchFlowResp.params
new file mode 100755
index 0000000..a413601
--- /dev/null
+++ b/TestON/tests/SCPFbatchFlowResp/SCPFbatchFlowResp.params
@@ -0,0 +1,72 @@
+
+<PARAMS>
+    <!--
+    # CASE - Descritpion
+    # 1,2,10,1000,1100,2000,1200,2000,100
+    # 1 - Variable initialization and optional pull and build ONOS package
+    # 2 - package onos, clean by uninstalling onos, install ONOS, start cli
+    # 10 - Start mininet and verify topology
+    # 11 - Start Null Provider linear topology
+    # 110 - check log for errors
+    # 1000 - build flow batches
+    # 2100 - REST POST flow batches in multiple threads and check till all ADDED
+    # 3100 - REST DELETE flow batches in multiple threads and check till all REMOVED
+    -->
+
+    <!-- <testcases>1,10,100,1000,100,2000,100,110</testcases> -->
+    <testcases>1,2,10,100,1000,2100,100,3100,100,110,210</testcases>
+
+    <GLOBAL>
+        <maxNodes>1</maxNodes> # currently only runs in single node onos
+        <numSw>63</numSw> # Number of devices used in topology
+        <numThreads>4</numThreads> #Number of Threads to use for POST and DELETE
+        <cluster>BM</cluster>
+        <SLEEP>
+            <startup>15</startup>
+            <startMN>15</startMN>
+            <addFlow>10</addFlow>
+            <delFlow>10</delFlow>
+            <chkFlow>0.5</chkFlow>
+            <cfg>5</cfg>
+        </SLEEP>
+    </GLOBAL>
+
+    <CASE1>
+        <cellName>temp</cellName>
+        <cellApps>drivers</cellApps>
+        <gitPull>False</gitPull>
+        <gitBranch>master</gitBranch>
+    </CASE1>
+
+    <CASE2>
+        <incPackaging>true</incPackaging>
+    </CASE2>
+
+    <CASE10>
+        <app>org.onosproject.openflow-base</app>
+        <adaptiveFlowenabled>false</adaptiveFlowenabled>
+        <mnArgs> --topo linear,{} --switch ovsk,protocols=OpenFlow13 --controller remote,port=6653</mnArgs>
+    </CASE10>
+
+    <CASE11>
+        <nullTopo>linear</nullTopo>
+        <nullStart>true</nullStart>
+    </CASE11>
+
+    <CASE1000>
+        <batchSize>200</batchSize>
+        <batches>500</batches>
+    </CASE1000>
+
+    <CASE2100>
+        <numThreads>4</numThreads>
+        <RESTchkFlow>main.FALSE</RESTchkFlow>
+        <chkFlowTO>200</chkFlowTO>
+    </CASE2100>
+
+    <CASE3100>
+        <RESTchkFlow>main.FALSE</RESTchkFlow>
+        <chkFlowTO>200</chkFlowTO>
+    </CASE3100>
+
+</PARAMS>
diff --git a/TestON/tests/SCPFbatchFlowResp/SCPFbatchFlowResp.py b/TestON/tests/SCPFbatchFlowResp/SCPFbatchFlowResp.py
new file mode 100755
index 0000000..ca5e240
--- /dev/null
+++ b/TestON/tests/SCPFbatchFlowResp/SCPFbatchFlowResp.py
@@ -0,0 +1,518 @@
+class SCPFbatchFlowResp:
+    '''
+    Testing end-to-end ONOS response time from POST of batched flows to when ONOS returns
+    response confirmation of all flows ADDED; subsequently testing the response time from when REST DELETE to
+    ONOS confirmation of all flows REMOVED.
+    '''
+
+    def __init__( self ):
+        self.default = ''
+
+    def CASE1( self, main ):
+        import time
+        import os
+        import imp
+
+        """
+        - Construct tests variables
+        - GIT ( optional )
+            - Checkout ONOS master branch
+            - Pull latest ONOS code
+        - Building ONOS ( optional )
+            - Install ONOS package
+            - Build ONOS package
+        """
+
+        main.case( "Constructing test variables and building ONOS package" )
+        main.step( "Constructing test variables" )
+
+        # Test variables
+        main.testOnDirectory = os.path.dirname( os.getcwd ( ) )
+        main.cellName = main.params[ 'CASE1' ][ 'cellName' ]
+        main.apps = main.params[ 'CASE1' ][ 'cellApps' ]
+        gitBranch = main.params[ 'CASE1' ][ 'gitBranch' ]
+        gitPull = main.params[ 'CASE1' ][ 'gitPull' ]
+        main.maxNodes = int( main.params['GLOBAL'][ 'maxNodes' ] )
+        main.startUpSleep = float( main.params['GLOBAL'][ 'SLEEP' ][ 'startup' ] )
+        main.startMNSleep = float( main.params['GLOBAL'][ 'SLEEP' ][ 'startMN' ] )
+        main.addFlowSleep = float( main.params['GLOBAL'][ 'SLEEP' ][ 'addFlow' ] )
+        main.delFlowSleep = float( main.params['GLOBAL'][ 'SLEEP' ][ 'delFlow' ] )
+        main.chkFlowSleep = float( main.params['GLOBAL']['SLEEP']['chkFlow'])
+        main.cfgSleep = float( main.params['GLOBAL']['SLEEP']['cfg'])
+        main.numSw = int( main.params['GLOBAL']['numSw'])
+        main.numThreads = int( main.params['GLOBAL']['numThreads'] )
+        main.cellData = {} # for creating cell file
+        main.CLIs = []
+        main.ONOSip = []
+        main.ONOSip = main.ONOSbench.getOnosIps()
+        main.commit = main.ONOSbench.getVersion()
+        main.cluster = main.params['GLOBAL']['cluster']
+
+        # Assigning ONOS cli handles to a list
+        for i in range( 1,  main.maxNodes + 1 ):
+            main.CLIs.append( getattr( main, 'ONOScli' + str( i ) ) )
+
+
+        if main.CLIs:
+            stepResult = main.TRUE
+        else:
+            main.log.error( "Did not properly created list of ONOS CLI handle" )
+            stepResult = main.FALSE
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully construct " +
+                                        "test variables ",
+                                 onfail="Failed to construct test variables" )
+
+        if gitPull == 'True':
+            main.step( "Building ONOS in " + gitBranch + " branch" )
+            onosBuildResult = main.startUp.onosBuild( main, gitBranch )
+            stepResult = onosBuildResult
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=stepResult,
+                                     onpass="Successfully compiled " +
+                                            "latest ONOS",
+                                     onfail="Failed to compile " +
+                                            "latest ONOS" )
+        else:
+            main.log.warn( "Did not pull new code so skipping mvn " +
+                           "clean install" )
+
+    def CASE2( self, main ):
+        """
+        - Set up cell
+            - Create cell file
+            - Set cell file
+            - Verify cell file
+        - Kill ONOS process
+        - Uninstall ONOS cluster
+        - Verify ONOS start up
+        - Install ONOS cluster
+        - Connect to cli
+        """
+
+        main.numCtrls = int( main.maxNodes )
+
+        main.case( "Starting up " + str( main.numCtrls ) +
+                   " node(s) ONOS cluster" )
+
+        main.log.info( "Safety check, killing all ONOS processes" +
+                       " before initiating environment setup" )
+
+        tempOnosIp = []
+        for i in range( main.numCtrls ):
+            tempOnosIp.append( main.ONOSip[i] )
+
+        if main.params['CASE2']['incPackaging'] == "true":
+            main.step("Create onos cell file with: " + main.apps)
+            main.ONOSbench.createCellFile( main.ONOSbench.ip_address, "temp",
+                                       main.Mininet1.ip_address, main.apps, tempOnosIp )
+
+            main.step( "Apply cell to environment" )
+            cellResult = main.ONOSbench.setCell( "temp" )
+            verifyResult = main.ONOSbench.verifyCell()
+            stepResult = cellResult and verifyResult
+            utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully applied cell to " + \
+                                        "environment",
+                                 onfail="Failed to apply cell to environment " )
+
+
+            main.step( "Creating ONOS package" )
+            packageResult = main.ONOSbench.onosPackage(opTimeout=240)
+            stepResult = packageResult
+            utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully created ONOS package",
+                                 onfail="Failed to create ONOS package" )
+            time.sleep( main.startUpSleep )
+
+            main.step( "Uninstalling ONOS package" )
+            onosUninstallResult = main.TRUE
+            for i in range( main.numCtrls ):
+                onosUninstallResult = onosUninstallResult and \
+                    main.ONOSbench.onosUninstall( nodeIp=main.ONOSip[ i ] )
+            stepResult = onosUninstallResult
+            utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully uninstalled ONOS package",
+                                 onfail="Failed to uninstall ONOS package" )
+            time.sleep( main.startUpSleep )
+
+        else:
+            main.log.info("onos Packaging Skipped!")
+
+        main.step( "Installing ONOS package" )
+        onosInstallResult = main.TRUE
+        for i in range( main.numCtrls ):
+            onosInstallResult = onosInstallResult and \
+                    main.ONOSbench.onosInstall( node=main.ONOSip[ i ] )
+        stepResult = onosInstallResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully installed ONOS package",
+                                 onfail="Failed to install ONOS package" )
+        time.sleep( main.startUpSleep )
+
+        main.step( "Starting ONOS service" )
+        stopResult = main.TRUE
+        startResult = main.TRUE
+        onosIsUp = main.TRUE
+
+        for i in range( main.numCtrls ):
+            onosIsUp = onosIsUp and main.ONOSbench.isup( main.ONOSip[ i ] )
+        if onosIsUp == main.TRUE:
+            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 = stopResult and \
+                        main.ONOSbench.onosStop( main.ONOSip[ i ] )
+            for i in range( main.numCtrls ):
+                startResult = startResult and \
+                        main.ONOSbench.onosStart( main.ONOSip[ i ] )
+        stepResult = onosIsUp and stopResult and startResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="ONOS service is ready",
+                                 onfail="ONOS service did not start properly" )
+
+        main.step( "Start ONOS cli" )
+        cliResult = main.TRUE
+        for i in range( i, main.numCtrls ):
+            cliResult = cliResult and \
+                        main.CLIs[ i ].startOnosCli( ONOSIp=main.ONOSip[ i ] )
+            main.log.info("ONOSip is: " + main.ONOSip[i])
+        stepResult = cliResult
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully start ONOS cli",
+                                 onfail="Failed to start ONOS cli" )
+
+
+
+    def CASE10( self, main ):
+        '''
+            Start Mininet
+        '''
+        import time
+
+        main.case( "Enable openflow-base on onos and start Mininet." )
+
+        main.step("Activate openflow-base App")
+        app = main.params['CASE10']['app']
+        stepResult = main.ONOSbench.onosCli( ONOSIp = main.ONOSip[0],
+                                             cmdstr = "app activate " + app )
+        time.sleep(main.cfgSleep)
+        main.log.info(stepResult)
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully activate " + app,
+                                 onfail="Failed to activate app " + app )
+
+        time.sleep(main.cfgSleep)
+
+
+        main.step( "Disable AdaptiveFlowSampling ")
+        stepResult = main.ONOSbench.onosCfgSet( main.ONOSip[0], "org.onosproject.provider.of.flow.impl.OpenFlowRuleProvider",
+                                   "adaptiveFlowSampling " + main.params['CASE10']['adaptiveFlowenabled'])
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="App Configuration Succeeded! ",
+                                 onfail="App Configuration Failed!" )
+        time.sleep(main.cfgSleep)
+
+        main.step( "Setup Mininet Linear Topology with " + str(main.numSw) + " switches" )
+        argStr = main.params['CASE10']['mnArgs'].format(main.numSw)
+        stepResult = main.Mininet1.startNet( args = argStr )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully loaded topology",
+                                 onfail="Failed to load topology" )
+
+        time.sleep( main.startMNSleep )
+
+        main.step( "Assign switches to controller" )
+        for i in range(1, main.numSw + 1):
+            main.Mininet1.assignSwController( "s" + str(i), main.ONOSip[0] )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully assigned switch to controller",
+                                 onfail="Failed to assign switch to controller" )
+
+        main.deviceIdPrefix = "of:"
+
+        time.sleep( main.startMNSleep )
+
+    def CASE11( self, main ):
+        '''
+            Start Null Provider
+        '''
+        import time
+
+        main.case( "Setup Null Provider for linear Topology" )
+
+        main.step("Activate Null Provider App")
+        stepResult = main.ONOSbench.onosCli( ONOSIp = main.ONOSip[0],
+                                             cmdstr = "app activate org.onosproject.null" )
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Successfully activated org.onosproject.null",
+                                 onfail="Failed to activate org.onosproject.null" )
+        time.sleep( main.cfgSleep)
+
+        main.step( "Setup Null Provider Linear Topology with " + str(main.numSw) + " devices." )
+        r1 = main.ONOSbench.onosCfgSet( main.ONOSip[0], "org.onosproject.provider.nil.NullProviders", "deviceCount " + str(main.numSw))
+        r2 = main.ONOSbench.onosCfgSet( main.ONOSip[0], "org.onosproject.provider.nil.NullProviders", "topoShape " + main.params['CASE11']['nullTopo'] )
+        r3 = main.ONOSbench.onosCfgSet( main.ONOSip[0], "org.onosproject.provider.nil.NullProviders", "enabled " + main.params['CASE11']['nullStart'])
+        stepResult = r1 & r2 & r3
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="App Configuration Succeeded! ",
+                                 onfail="App Configuration Failed!" )
+        time.sleep( main.cfgSleep )
+
+        main.log.info("Check to make sure null providers are configured correctly.")
+        main.ONOSbench.handle.sendline("onos $OC1 summary")
+        stepResult = main.ONOSbench.handle.expect(":~")
+        main.log.info("ONOS Summary: " + main.ONOSbench.handle.before)
+
+        main.deviceIdPrefix = "null:"
+
+        time.sleep( main.startMNSleep )
+
+
+
+    def CASE1000( self, main ):
+        '''
+            create JSON object with batched flows
+        '''
+        import numpy
+        import time
+        from pprint import pprint
+
+        main.case( "Create a json object for the batched flows" )
+
+        main.step( "Parse batch creation information" )
+        main.batchSize = int(main.params['CASE1000']['batchSize'])
+        main.log.info("Number of flows in a batch is:" + str(main.batchSize))
+
+        main.flowJsonBatchList = []
+        startSw = 1
+
+        main.step("Creating a full list of batches")
+        for index in range(1, int(main.params['CASE1000']['batches']) + 1):
+            if startSw <= main.numSw:
+                ind = startSw
+            else:
+                startSw = 1
+                ind = startSw
+
+            main.log.info("Creating batch: " + str(index))
+            flowJsonBatch = main.ONOSrest.createFlowBatch( numSw = main.numSw,
+                                                           swIndex = ind,
+                                                           batchSize = main.batchSize,
+                                                           batchIndex = index,
+                                                           deviceIdpreFix=main.deviceIdPrefix,
+                                                           ingressPort = 2,
+                                                           egressPort = 3)
+            main.flowJsonBatchList.append(flowJsonBatch)
+
+            startSw += 1
+        main.log.info( "Number of items created in the batch list is: " + str(len(main.flowJsonBatchList)))
+
+    def CASE2100(self, main):
+        '''
+            Posting flow batches using threads
+        '''
+        main.case("Using REST API /flows/{} to post flow batch - multi-threads")
+        main.step("Using REST API /flows/{} to post flow batch - multi-threads")
+
+        from Queue import Queue
+        from threading import Thread
+        import time
+        import json
+
+        main.threadID = 0
+        main.addedBatchList = []
+        q = Queue()
+        tAllAdded = 0
+
+        def postWorker(id):
+            while True:
+                item = q.get()
+                #print json.dumps(item)
+                status,response = main.ONOSrest.sendFlowBatch(batch = item)
+                main.log.info("Thread {} is working on posting. ".format(id))
+                #print json.dumps(response)
+                main.addedBatchList.append(response[1])
+                q.task_done()
+
+        for i in range( int( main.params['CASE2100']['numThreads'])):
+            threadID = "ThreadID-" + str(i)
+            t = Thread(target = postWorker, name = threadID, args=(threadID,) )
+            t.daemon = True
+            t.start()
+
+        tStartPost = time.time()
+        for item in main.flowJsonBatchList:
+            q.put(item)
+
+        q.join()
+        tLastPostEnd = time.time()
+
+        main.step("Check to ensure all flows are in added state.")
+        #pprint(main.addedBatchList)
+        resp = main.FALSE
+        while resp != main.TRUE and ( tAllAdded - tLastPostEnd < int (main.params['CASE2100']['chkFlowTO']) ):
+            if main.params['CASE2100']['RESTchkFlow'] == main.TRUE:
+                resp = main.ONOSrest.checkFlowsState()
+            else:
+                handle = main.CLIs[0].flows(state = " |grep PEND|wc -l", jsonFormat=False)
+                main.log.info("handle returns PENDING flows: " + handle)
+                if handle == "0":
+                    resp = main.TRUE
+
+            time.sleep( main.chkFlowSleep )
+            tAllAdded = time.time()
+
+        if tAllAdded - tLastPostEnd >= int (main.params['CASE2100']['chkFlowTO']):
+            main.log.warn("ONOS Flows still in pending state after: {} seconds.".format(tAllAdded - tLastPostEnd))
+
+        main.numFlows = int(main.params['CASE1000']['batches']) *\
+                                                    int(main.params['CASE1000']['batchSize'])
+        main.log.info("Total number of flows: " + str (main.numFlows) )
+        main.elapsePOST = tLastPostEnd-tStartPost
+        main.log.info("Total POST elapse time: " + str( main.elapsePOST ) )
+        main.log.info("Rate of ADD Controller response: " + str(main.numFlows / ( main.elapsePOST )))
+
+        main.POSTtoCONFRM = tAllAdded - tLastPostEnd
+        main.log.info("Elapse time from end of last REST POST to Flows in ADDED state: " +\
+                      str( main.POSTtoCONFRM ))
+        main.log.info("Rate of Confirmed Batch Flow ADD is (flows/sec): " +
+                      str( main.numFlows / main.POSTtoCONFRM ))
+        main.log.info("Number of flow Batches in the addedBatchList is: " +
+                      str( len(main.addedBatchList)))
+
+    def CASE3100(self, main):
+        '''
+            DELETE flow batches using threads
+        '''
+        main.case("Using REST API /flows/{} to delete flow batch - multi-threads")
+        main.step("Using REST API /flows/{} to delete flow batch - multi-threads")
+
+        from Queue import Queue
+        from threading import Thread
+        import time
+        import json
+
+        main.threadID = 0
+        q = Queue()
+        tAllRemoved = 0
+
+        main.log.info("Number of flow batches at start of remove: " + str( len( main.addedBatchList)))
+        def removeWorker(id):
+            while True:
+                item = q.get()
+                response = main.ONOSrest.removeFlowBatch(batch = json.loads(item) )
+                main.log.info("Thread {} is working on deleting. ".format(id))
+                q.task_done()
+
+        for i in range( int( main.params['CASE2100']['numThreads'])):
+            threadID = "ThreadID-" + str(i)
+            t = Thread(target = removeWorker, name = threadID, args=(threadID,) )
+            t.daemon = True
+            t.start()
+
+        tStartDelete = time.time()
+        for item in main.addedBatchList:
+            q.put(item)
+
+        q.join()
+        tLastDeleteEnd = time.time()
+        main.log.info("Number of flow batches at end of remove: " + str( len( main.addedBatchList)))
+
+        main.step("Check to ensure all flows are in added state.")
+        #pprint(main.addedBatchList)
+        resp = main.FALSE
+        while resp != main.TRUE and ( tAllRemoved - tLastDeleteEnd < int (main.params['CASE3100']['chkFlowTO']) ):
+            if main.params['CASE3100']['RESTchkFlow'] == main.TRUE:
+                resp = main.ONOSrest.checkFlowsState()
+            else:
+                handle = main.CLIs[0].flows(state = " |grep PEND|wc -l", jsonFormat=False)
+                main.log.info("handle returns PENDING flows: " + handle)
+                if handle == "0":
+                    resp = main.TRUE
+            time.sleep( main.chkFlowSleep )
+            tAllRemoved = time.time()
+
+        if tLastDeleteEnd - tLastDeleteEnd >= int (main.params['CASE2100']['chkFlowTO']):
+            main.log.warn("ONOS Flows still in pending state after: {} seconds.".format(tAllRemoved - tLastDeleteEnd))
+
+        main.numFlows = int(main.params['CASE1000']['batches']) *\
+                                                    int(main.params['CASE1000']['batchSize'])
+        main.log.info("Total number of flows: " + str (main.numFlows) )
+        main.elapseDELETE = tLastDeleteEnd-tStartDelete
+        main.log.info("Total DELETE elapse time: " + str( main.elapseDELETE ))
+        main.log.info("Rate of DELETE Controller response: " + str(main.numFlows / ( main.elapseDELETE )))
+
+        main.DELtoCONFRM = tAllRemoved - tLastDeleteEnd
+        main.log.info("Elapse time from end of last REST DELETE to Flows in REMOVED state: " +\
+                      str( main.DELtoCONFRM ))
+        main.log.info("Rate of Confirmed Batch Flow REMOVED is (flows/sec): " + str( main.numFlows / main.DELtoCONFRM))
+
+    def CASE100(self,main):
+        from pprint import pprint
+
+        main.case( "Check to ensure onos flows." )
+
+        resp = main.ONOSrest.checkFlowsState()
+        #pprint(resp)
+
+    def CASE210(self,main):
+        main.case("Log test results to a data file")
+        main.step("Write test resulted data to a data file")
+        main.scale = main.maxNodes
+
+        try:
+            dbFileName="/tmp/SCPFbatchFlowRespData"
+            dbfile = open(dbFileName, "w+")
+            temp = "'" + main.commit + "',"
+            temp += "'" + str( main.scale ) + "',"
+            temp += "'" + main.cluster + "',"
+            temp += "'" + str( main.elapsePOST ) + "',"
+            temp += "'" + str( main.POSTtoCONFRM ) + "',"
+            temp += "'" + str ( main.elapseDELETE ) + "',"
+            temp += "'" + str ( main.DELtoCONFRM ) + "'\n"
+            dbfile.write( temp )
+            dbfile.close()
+            stepResult = main.TRUE
+        except IOError:
+            main.log.warn("Error opening " + dbFileName + " to write results.")
+            stepResult = main.FALSE
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="Succeeded to write results to datafile",
+                                 onfail="Failed to write results to datafile " )
+        
+    def CASE110( self, main ):
+        '''
+        Report errors/warnings/exceptions
+        '''
+        main.log.info("Error report: \n" )
+        main.ONOSbench.logReport( main.ONOSip[ 0 ],
+                                  [ "INFO",
+                                    "FOLLOWER",
+                                    "WARN",
+                                    "flow",
+                                    "ERROR",
+                                    "Except" ],
+                                  "s" )
+        #main.stop()
+
diff --git a/TestON/tests/SCPFbatchFlowResp/SCPFbatchFlowResp.topo b/TestON/tests/SCPFbatchFlowResp/SCPFbatchFlowResp.topo
new file mode 100755
index 0000000..0e3543e
--- /dev/null
+++ b/TestON/tests/SCPFbatchFlowResp/SCPFbatchFlowResp.topo
@@ -0,0 +1,46 @@
+<TOPOLOGY>
+    <COMPONENT>
+
+        <ONOSbench>
+            <host>localhost</host>
+            <user>admin</user>
+            <password>onos_test</password>
+            <type>OnosDriver</type>
+            <connect_order>1</connect_order>
+            <COMPONENTS>
+                <home>~/Projects/onos</home>
+            </COMPONENTS>
+        </ONOSbench>
+
+        <ONOScli1>
+            <host>localhost</host>
+            <user>admin</user>
+            <password>onos_test</password>
+            <type>OnosCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOScli1>
+
+        <Mininet1>
+            <host>localhost</host>
+            <user>admin</user>
+            <password>onos_test</password>
+            <type>MininetCliDriver</type>
+            <connect_order>5</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </Mininet1>
+
+        <ONOSrest>
+            <host>OC1</host>
+            <port>8181</port>
+            <user>onos</user>
+            <password>rocks</password>
+            <type>OnosRestDriver</type>
+            <connect_order>6</connect_order>
+            <COMPONENTS>
+            </COMPONENTS>
+        </ONOSrest>
+
+    </COMPONENT>
+</TOPOLOGY>
diff --git a/TestON/tests/SCPFbatchFlowResp/__init__.py b/TestON/tests/SCPFbatchFlowResp/__init__.py
new file mode 100755
index 0000000..e69de29
--- /dev/null
+++ b/TestON/tests/SCPFbatchFlowResp/__init__.py