Merge "SCPFswitchLat initial commit"
diff --git a/TestON/drivers/common/api/controller/onosrestdriver.py b/TestON/drivers/common/api/controller/onosrestdriver.py
index b948ce5..d7e0572 100644
--- a/TestON/drivers/common/api/controller/onosrestdriver.py
+++ b/TestON/drivers/common/api/controller/onosrestdriver.py
@@ -19,6 +19,7 @@
 import json
 import os
 import requests
+import types
 
 from drivers.common.api.controllerdriver import Controller
 
@@ -43,7 +44,7 @@
             if os.getenv( str( self.ip_address ) ) != None:
                 self.ip_address = os.getenv( str( self.ip_address ) )
             else:
-                main.log.info( self.name + ": ip set to" + self.ip_address )
+                main.log.info( self.name + ": ip set to " + self.ip_address )
         except KeyError:
             main.log.info( "Invalid host name," +
                            "defaulting to 'localhost' instead" )
@@ -65,7 +66,7 @@
             str base: The base url for the given REST api. Applications could
                       potentially have their own base url
             str method: HTTP method type
-            dict params: Dictionary to be sent in the query string for
+            dict query: Dictionary to be sent in the query string for
                          the request
             dict data: Dictionary to be sent in the body of the request
         """
@@ -88,10 +89,16 @@
         except Exception as e:
             main.log.exception( e )
             return None
-        # FIXME: add other exceptions  
+        # FIXME: add other exceptions
 
     def intents( self, ip="DEFAULT", port="DEFAULT" ):
         """
+        Description:
+            Gets a list of dictionary of all intents in the system
+        Returns:
+            A list of dictionary of intents in string type to match the cli
+            version for now; Returns main.FALSE if error on request;
+            Returns None for exception
         """
         try:
             output = None
@@ -99,71 +106,40 @@
                 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" )
+                main.log.warn( "No port given, reverting to port " +
+                               "from topo file" )
                 port = self.port
             response = self.send( ip, port, url="/intents" )
             if response:
-                main.log.debug( response )
-                # We can do some verification on the return code
-                # NOTE: Not all requests return 200 on success, usually it will be a 2XX code
-                #       3XX is a redirction, which may be given on a post to show where the new resource can be found
-                #       4XX is usually a bad request on our side
-                #       5XX will usually mean an ONOS bug
-                if response[0] == 200:
-                    main.log.debug("all ok")
+                if 200 <= response[ 0 ] <= 299:
+                    output = response[ 1 ]
+                    a = json.loads( output ).get( 'intents' )
+                    b = json.dumps( a )
+                    return b
                 else:
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
-                    # return main.FALSE
-                output = response[1]
-
-            # NOTE: The output is slightly differen from the cli.
-            # if data = cli's json output
-            # then rest output is dict( 'intents' : data )
-
-            # We have some options here:
-            # 1) we can return the whole thing as a string as we do with cli,
-            #    then change our tests to correctly parse the data
-            #
-            # 2) We could return just the data of the response, but we would
-            #    have to decide if we return:
-            # 2a) the data as a dictionary or
-            # 2b) as a json string.
-            #
-            # If we return the dict, we probably want to change the
-            #    cli drivers to match.
-
-            # Ideally, we would be able to switch between using the api driver
-            # and the cli driver by simply changing the .topo file. I don't
-            # know if we will achive that right away, but we should try to plan
-            # for that.
-
-
-            # 1)
-            '''
-            return output
-            '''
-
-            # 2a)
-            '''
-            import json
-            a = json.loads( output )
-            return a.get( 'intents' )
-            '''
-
-            # 2b)
-            import json
-            a = json.loads( output ).get( 'intents' )
-            b = json.dumps(a)
-            main.log.debug( b )
-            return b
+                    return main.FALSE
         except Exception as e:
             main.log.exception( e )
             return None
 
-    def intent( self, appId, intentId,  ip="DEFAULT", port="DEFAULT" ):
+    def intent( self, intentId, appId="org.onosproject.cli",
+                ip="DEFAULT", port="DEFAULT" ):
         """
-        Returns a single intent in dictionary form
+        Description:
+            Get the specific intent information of the given application ID and
+            intent ID
+        Required:
+            str intentId - Intent id in hexadecimal form
+        Optional:
+            str appId - application id of intent
+        Returns:
+            Returns an information dictionary of the given intent;
+            Returns main.FALSE if error on requests; Returns None for exception
+        NOTE:
+            The GET /intents REST api command accepts  application id but the
+            api will get updated to accept application name instead
         """
         try:
             output = None
@@ -171,27 +147,802 @@
                 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" )
+                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
-            query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
+            query = "/" + str( appId ) + "/" + str( intentId )
             response = self.send( ip, port, url="/intents" + query )
             if response:
-                main.log.debug( response )
-                # We can do some verification on the return code
-                # NOTE: Not all requests return 200 on success, usually it will be a 2XX code
-                #       3XX is a redirction, which may be given on a post to show where the new resource can be found
-                #       4XX is usually a bad request on our side
-                #       5XX will usually mean an ONOS bug
-                if response[0] == 200:
-                    main.log.debug("all ok")
+                if 200 <= response[ 0 ] <= 299:
+                    output = response[ 1 ]
+                    a = json.loads( output )
+                    return a
                 else:
                     main.log.error( "Error with REST request, response was: " +
                                     str( response ) )
-                    # return main.FALSE
-                output = response[1]
-            a = json.loads( output )
-            return a
+                    return main.FALSE
         except Exception as e:
             main.log.exception( e )
             return None
+
+    def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
+        """
+        Description:
+           Gets all intents ID using intents function
+        Returns:
+            List of intents ID;Returns None for exception; Returns None for
+            exception; Returns None for exception
+        """
+        try:
+            intentsDict = {}
+            intentsIdList = []
+            intentsDict = json.loads( self.intents( ip=ip, port=port ) )
+            for intent in intentsDict:
+                intentsIdList.append( intent.get( 'id' ) )
+
+            if not intentsIdList:
+                main.log.debug( "Cannot find any intents" )
+                return main.FALSE
+            else:
+                main.log.info( "Found intents: " + str( intentsIdList ) )
+                return main.TRUE
+
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+
+    def apps( self, ip="DEFAULT", port="DEFAULT" ):
+        """
+        Description:
+            Returns all the current application installed in the system
+        Returns:
+            List of dictionary of installed application; Returns main.FALSE for
+            error on request; Returns None for exception
+        """
+        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
+            response = self.send( ip, port, url="/applications" )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    output = response[ 1 ]
+                    a = json.loads( output ).get( 'applications' )
+                    b = json.dumps( a )
+                    return b
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+    def activateApp( self, appName, ip="DEFAULT", port="DEFAULT", check=True ):
+        """
+        Decription:
+            Activate an app that is already installed in ONOS
+        Optional:
+            bool check - If check is True, method will check the status
+                         of the app after the command is issued
+        Returns:
+            Returns main.TRUE if the command was successfully or main.FALSE
+            if the REST responded with an error or given incorrect input;
+            Returns None for exception
+
+        """
+        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
+            query = "/" + str( appName ) + "/active"
+            response = self.send( ip, port, method="POST",
+                                  url="/applications" + query )
+            if response:
+                output = response[ 1 ]
+                app = json.loads( output )
+                if 200 <= response[ 0 ] <= 299:
+                    if check:
+                        if app.get( 'state' ) == 'ACTIVE':
+                            main.log.info( self.name + ": " + appName +
+                                           " application" +
+                                           " is in ACTIVE state" )
+                            return main.TRUE
+                        else:
+                            main.log.error( self.name + ": " + appName +
+                                            " application" + " is in " +
+                                            app.get( 'state' ) + " state" )
+                            return main.FALSE
+                    else:
+                        main.log.warn( "Skipping " + appName +
+                                       "application check" )
+                        return main.TRUE
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+    def deactivateApp( self, appName, ip="DEFAULT", port="DEFAULT",
+                       check=True ):
+        """
+        Required:
+            Deactivate an app that is already activated in ONOS
+        Optional:
+            bool check - If check is True, method will check the status of the
+            app after the command is issued
+        Returns:
+            Returns main.TRUE if the command was successfully sent
+            main.FALSE if the REST responded with an error or given
+            incorrect input; Returns None for exception
+        """
+        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
+            query = "/" + str( appName ) + "/active"
+            response = self.send( ip, port, method="DELETE",
+                                  url="/applications" + query )
+            if response:
+                output = response[ 1 ]
+                app = json.loads( output )
+                if 200 <= response[ 0 ] <= 299:
+                    if check:
+                        if app.get( 'state' ) == 'INSTALLED':
+                            main.log.info( self.name + ": " + appName +
+                                           " application" +
+                                           " is in INSTALLED state" )
+                            return main.TRUE
+                        else:
+                            main.log.error( self.name + ": " + appName +
+                                            " application" + " is in " +
+                                            app.get( 'state' ) + " state" )
+                            return main.FALSE
+                    else:
+                        main.log.warn( "Skipping " + appName +
+                                       "application check" )
+                        return main.TRUE
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+    def getApp( self, appName, project="org.onosproject.", ip="DEFAULT",
+                port="DEFAULT" ):
+        """
+        Decription:
+            Gets the informaion of the given application
+        Required:
+            str name - Name of onos application
+        Returns:
+            Returns a dictionary of information ONOS application in string type;
+            Returns main.FALSE if error on requests; Returns None for exception
+        """
+        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
+            query = "/" + project + str( appName )
+            response = self.send( ip, port, url="/applications" + query )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    output = response[ 1 ]
+                    a = json.loads( output )
+                    return a
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+    def addHostIntent( self, hostIdOne, hostIdTwo, appId='org.onosproject.cli',
+                       ip="DEFAULT", port="DEFAULT" ):
+        """
+        Description:
+            Adds a host-to-host intent ( bidirectional ) by
+            specifying the two hosts.
+        Required:
+            * hostIdOne: ONOS host id for host1
+            * hostIdTwo: ONOS host id for host2
+        Optional:
+            str appId - Application name of intent identifier
+        Returns:
+            Returns main.TRUE for successful requests; Returns main.FALSE if
+            error on requests; Returns None for exceptions
+        """
+        try:
+            intentJson = {"two": str( hostIdTwo ),
+                          "selector": {"criteria": []}, "priority": 7,
+                          "treatment": {"deferred": [], "instructions": []},
+                          "appId": appId, "one": str( hostIdOne ),
+                          "type": "HostToHostIntent",
+                          "constraints": [{"type": "LinkTypeConstraint",
+                                           "types": ["OPTICAL"],
+                                           "inclusive": 'false' }]}
+            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
+            response = self.send( ip,
+                                  port,
+                                  method="POST",
+                                  url="/intents",
+                                  data=json.dumps( intentJson ) )
+            if response:
+                if 201:
+                    main.log.info( self.name + ": Successfully POST host" +
+                                   " intent between host: " + hostIdOne +
+                                   " and host: " + hostIdTwo )
+                    return main.TRUE
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+    def addPointIntent( self,
+                        ingressDevice,
+                        egressDevice,
+                        ip="DEFAULT",
+                        port="DEFAULT",
+                        appId='org.onosproject.cli',
+                        ingressPort="",
+                        egressPort="",
+                        ethType="",
+                        ethSrc="",
+                        ethDst="",
+                        bandwidth="",
+                        lambdaAlloc=False,
+                        ipProto="",
+                        ipSrc="",
+                        ipDst="",
+                        tcpSrc="",
+                        tcpDst="" ):
+        """
+        Description:
+            Adds a point-to-point intent ( uni-directional ) by
+            specifying device id's and optional fields
+        Required:
+            * ingressDevice: device id of ingress device
+            * egressDevice: device id of egress device
+        Optional:
+            * ethType: specify ethType
+            * ethSrc: specify ethSrc ( i.e. src mac addr )
+            * ethDst: specify ethDst ( i.e. dst mac addr )
+            * bandwidth: specify bandwidth capacity of link (TODO)
+            * lambdaAlloc: if True, intent will allocate lambda
+              for the specified intent (TODO)
+            * ipProto: specify ip protocol
+            * ipSrc: specify ip source address with mask eg. ip#/24
+            * ipDst: specify ip destination address eg. ip#/24
+            * tcpSrc: specify tcp source port
+            * tcpDst: specify tcp destination port
+        Returns:
+            Returns main.TRUE for successful requests; Returns main.FALSE if
+            no ingress|egress port found and 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
+        """
+        try:
+            if "/" in ingressDevice:
+                if not ingressPort:
+                    ingressPort = ingressDevice.split( "/" )[ 1 ]
+                ingressDevice = ingressDevice.split( "/" )[ 0 ]
+            else:
+                if not ingressPort:
+                    main.log.debug( self.name + ": Ingress port not specified" )
+                    return main.FALSE
+
+            if "/" in egressDevice:
+                if not egressPort:
+                    egressPort = egressDevice.split( "/" )[ 1 ]
+                egressDevice = egressDevice.split( "/" )[ 0 ]
+            else:
+                if not egressPort:
+                    main.log.debug( self.name + ": Egress port not specified" )
+                    return main.FALSE
+
+            intentJson ={ "ingressPoint": { "device": ingressDevice,
+                                           "port": ingressPort },
+                          "selector": { "criteria": [] },
+                          "priority": 55,
+                          "treatment": { "deferred": [],
+                                         "instructions": [] },
+                          "egressPoint": { "device": egressDevice,
+                                           "port": egressPort },
+                          "appId": appId,
+                          "type": "PointToPointIntent",
+                          "constraints": [ { "type": "LinkTypeConstraint",
+                                             "types": [ "OPTICAL" ],
+                                             "inclusive": "false" } ] }
+
+            if ethType == "IPV4":
+                intentJson[ 'selector' ][ 'criteria' ].append( {
+                                                         "type":"ETH_TYPE",
+                                                         "ethType":2048 } )
+            if ethSrc:
+                intentJson[ 'selector' ][ 'criteria' ].append(
+                                                       { "type":"ETH_SRC",
+                                                         "mac":ethSrc } )
+            if ethDst:
+                intentJson[ 'selector' ][ 'criteria' ].append(
+                                                       { "type":"ETH_DST",
+                                                         "mac":ethDst } )
+            if ipSrc:
+                intentJson[ 'selector' ][ 'criteria' ].append(
+                                                       { "type":"IPV4_SRC",
+                                                         "ip":ipSrc } )
+            if ipDst:
+                intentJson[ 'selector' ][ 'criteria' ].append(
+                                                       { "type":"IPV4_DST",
+                                                         "ip":ipDst } )
+            if tcpSrc:
+                intentJson[ 'selector' ][ 'criteria' ].append(
+                                                       { "type":"TCP_SRC",
+                                                         "tcpPort": tcpSrc } )
+            if tcpDst:
+                intentJson[ 'selector' ][ 'criteria' ].append(
+                                                       { "type":"TCP_DST",
+                                                         "tcpPort": tcpDst } )
+            if ipProto:
+                intentJson[ 'selector' ][ 'criteria' ].append(
+                                                       { "type":"IP_PROTO",
+                                                         "protocol": ipProto } )
+
+            # TODO: Bandwidth and Lambda will be implemented if needed
+
+            main.log.debug( intentJson )
+
+            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
+            response = self.send( ip,
+                                  port,
+                                  method="POST",
+                                  url="/intents",
+                                  data=json.dumps( intentJson ) )
+            if response:
+                if 201:
+                    main.log.info( self.name + ": Successfully POST point" +
+                                   " intent between ingress: " + ingressDevice +
+                                   " and egress: " + egressDevice + " devices" )
+                    return main.TRUE
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+
+    def removeIntent( self, intentId, appId='org.onosproject.cli',
+                       ip="DEFAULT", port="DEFAULT" ):
+        """
+        Remove intent for specified application id and intent id;
+        Returns None for exception
+        """
+        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
+            query = "/" + str( appId ) + "/" + str( int( intentId, 16 ) )
+            response = self.send( ip,
+                                  port,
+                                  method="DELETE",
+                                  url="/intents" + query )
+            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 Exception as e:
+            main.log.exception( e )
+            return None
+
+    def getIntentsId( self, ip="DEFAULT", port="DEFAULT" ):
+        """
+        Returns a list of intents id; Returns None for exception
+        """
+        try:
+            intentIdList = []
+            intentsJson = json.loads( self.intents() )
+            print intentsJson
+            for intent in intentsJson:
+                intentIdList.append( intent.get( 'id' ) )
+            print intentIdList
+            return intentIdList
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+    def removeAllIntents( self, intentIdList ='ALL',appId='org.onosproject.cli',
+                          ip="DEFAULT", port="DEFAULT", delay=5 ):
+        """
+        Description:
+            Remove all the intents
+        Returns:
+            Returns main.TRUE if all intents are removed, otherwise returns
+            main.FALSE; Returns None for exception
+        """
+        try:
+            results = []
+            if intentIdList == 'ALL':
+                intentIdList = self.getIntentsId( ip=ip, port=port )
+
+            main.log.info( self.name + ": Removing intents " +
+                           str( intentIdList ) )
+
+            if isinstance( intentIdList, types.ListType ):
+                for intent in intentIdList:
+                    results.append( self.removeIntent( intentId=intent,
+                                                       appId=appId,
+                                                       ip=ip,
+                                                       port=port ) )
+                # Check for remaining intents
+                # NOTE: Noticing some delay on Deleting the intents so i put
+                # this time out
+                import time
+                time.sleep( delay )
+                intentRemain = len( json.loads( self.intents() ) )
+                if all( result==main.TRUE for result in results ) and \
+                   intentRemain == 0:
+                    main.log.info( self.name + ": All intents are removed " )
+                    return main.TRUE
+                else:
+                    main.log.error( self.name + ": Did not removed all intents,"
+                                    + " there are " + str( intentRemain )
+                                    + " intents remaining" )
+                    return main.FALSE
+            else:
+                main.log.debug( self.name + ": There is no intents ID list" )
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+
+    def hosts( self, ip="DEFAULT", port="DEFAULT" ):
+        """
+        Description:
+            Get a list of dictionary of all discovered hosts
+        Returns:
+            Returns a list of dictionary of information of the hosts currently
+            discovered by ONOS; Returns main.FALSE if error on requests;
+            Returns None for exception
+        """
+        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
+            response = self.send( ip, port, url="/hosts" )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    output = response[ 1 ]
+                    a = json.loads( output ).get( 'hosts' )
+                    b = json.dumps( a )
+                    return b
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+    def getHost( self, mac, vlan="-1", ip="DEFAULT", port="DEFAULT" ):
+        """
+        Description:
+            Gets the information from the given host
+        Required:
+            str mac - MAC address of the host
+        Optional:
+            str vlan - VLAN tag of the host, defaults to -1
+        Returns:
+            Return the host id from the hosts/mac/vlan output in REST api
+            whose 'id' contains mac/vlan; Returns None for exception;
+            Returns main.FALSE if error on requests
+
+        NOTE:
+            Not sure what this function should do, any suggestion?
+        """
+        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
+            query = "/" + mac + "/" + vlan
+            response = self.send( ip, port, url="/hosts" + query )
+            if response:
+            # NOTE: What if the person wants other values? would it be better
+            # to have a function that gets a key and return a value instead?
+            # This function requires mac and vlan and returns an ID which
+            # makes this current function useless
+                if 200 <= response[ 0 ] <= 299:
+                    output = response[ 1 ]
+                    hostId = json.loads( output ).get( 'id' )
+                    return hostId
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+    def topology( self, ip="DEFAULT", port="DEFAULT" ):
+        """
+        Description:
+            Gets the overview of network topology
+        Returns:
+            Returns a dictionary containing information about network topology;
+            Returns None for exception
+        """
+        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
+            response = self.send( ip, port, url="/topology" )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    output = response[ 1 ]
+                    a = json.loads( output )
+                    b = json.dumps( a )
+                    return b
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+    def getIntentState( self, intentsId, intentsJson=None,
+                        ip="DEFAULT", port="DEFAULT" ):
+        """
+        Description:
+            Get intent state.
+            Accepts a single intent ID (string type) or a list of intent IDs.
+            Returns the state(string type) of the id if a single intent ID is
+            accepted.
+        Required:
+            intentId: intent ID (string type)
+            intentsJson: parsed json object from the onos:intents api
+        Returns:
+            Returns a dictionary with intent IDs as the key and its
+            corresponding states as the values; Returns None for invalid IDs or
+            Type error and any exceptions
+        NOTE:
+            An intent's state consist of INSTALLED,WITHDRAWN etc.
+        """
+        try:
+            state = "State is Undefined"
+            if not intentsJson:
+                intentsJsonTemp = json.loads( self.intents() )
+            else:
+                intentsJsonTemp = json.loads( intentsJson )
+            if isinstance( intentsId, types.StringType ):
+                for intent in intentsJsonTemp:
+                    if intentsId == intent[ 'id' ]:
+                        state = intent[ 'state' ]
+                        return state
+                main.log.info( "Cannot find intent ID" + str( intentsId ) +
+                               " on the list" )
+                return state
+            elif isinstance( intentsId, types.ListType ):
+                dictList = []
+                for i in xrange( len( intentsId ) ):
+                    stateDict = {}
+                    for intents in intentsJsonTemp:
+                        if intentsId[ i ] == intents[ 'id' ]:
+                            stateDict[ 'state' ] = intents[ 'state' ]
+                            stateDict[ 'id' ] = intentsId[ i ]
+                            dictList.append( stateDict )
+                            break
+                if len( intentsId ) != len( dictList ):
+                    main.log.info( "Cannot find some of the intent ID state" )
+                return dictList
+            else:
+                main.log.info( "Invalid intents ID entry" )
+                return None
+
+        except TypeError:
+            main.log.exception( self.name + ": Object Type not as expected" )
+            return None
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+    def checkIntentState( self, intentsId="ALL", expectedState='INSTALLED',
+                          ip="DEFAULT", port="DEFAULT"):
+        """
+        Description:
+            Check intents state based on expected state which defaults to
+            INSTALLED state
+        Required:
+            intentsId - List of intents ID to be checked
+        Optional:
+            expectedState - Check the expected state(s) of each intents
+                            state in the list.
+                            *NOTE: You can pass in a list of expected state,
+                            Eg: expectedState = [ 'INSTALLED' , 'INSTALLING' ]
+        Return:
+            Returns main.TRUE only if all intent are the same as expected states
+            , otherwise, returns main.FALSE; Returns None for general exception
+        """
+        try:
+            # Generating a dictionary: intent id as a key and state as value
+            returnValue = main.TRUE
+            if intentsId == "ALL":
+                intentsId = self.getIntentsId( ip=ip, port=port )
+            intentsDict = self.getIntentState( intentsId, ip=ip, port=port )
+
+            #print "len of intentsDict ", str( len( intentsDict ) )
+            if len( intentsId ) != len( intentsDict ):
+                main.log.error( self.name + ": There is something wrong " +
+                                "getting intents state" )
+                return main.FALSE
+
+            if isinstance( expectedState, types.StringType ):
+                for intents in intentsDict:
+                    if intents.get( 'state' ) != expectedState:
+                        main.log.debug( self.name + " : Intent ID - " +
+                                        intents.get( 'id' ) +
+                                        " actual state = " +
+                                        intents.get( 'state' )
+                                        + " does not equal expected state = "
+                                        + expectedState )
+                        returnValue = main.FALSE
+
+            elif isinstance( expectedState, types.ListType ):
+                for intents in intentsDict:
+                    if not any( state == intents.get( 'state' ) for state in
+                                expectedState ):
+                        main.log.debug( self.name + " : Intent ID - " +
+                                        intents.get( 'id' ) +
+                                        " actual state = " +
+                                        intents.get( 'state' ) +
+                                        " does not equal expected states = "
+                                        + str( expectedState ) )
+                        returnValue = main.FALSE
+
+            if returnValue == main.TRUE:
+                main.log.info( self.name + ": All " +
+                               str( len( intentsDict ) ) +
+                               " intents are in " + str( expectedState ) +
+                               " state" )
+            return returnValue
+        except TypeError:
+            main.log.exception( self.name + ": Object not as expected" )
+            return main.FALSE
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+    def flows( self, ip="DEFAULT", port="DEFAULT" ):
+        """
+        Description:
+            Get flows currently added to the system
+        NOTE:
+            The flows -j cli command has completely different format than
+            the REST output; Returns None for exception
+        """
+        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
+            response = self.send( ip, port, url="/flows" )
+            if response:
+                if 200 <= response[ 0 ] <= 299:
+                    output = response[ 1 ]
+                    a = json.loads( output ).get( 'flows' )
+                    b = json.dumps( a )
+                    return b
+                else:
+                    main.log.error( "Error with REST request, response was: " +
+                                    str( response ) )
+                    return main.FALSE
+        except Exception as e:
+            main.log.exception( e )
+            return None
+
+    def checkFlowsState( self , ip="DEFAULT", port="DEFAULT" ):
+        """
+        Description:
+            Check if all the current flows are in ADDED state
+        Return:
+            returnValue - Returns main.TRUE only if all flows are in
+                          return main.FALSE otherwise;
+                          Returns None for exception
+        """
+        try:
+            tempFlows = json.loads( self.flows( ip=ip, port=port ) )
+            returnValue = main.TRUE
+            for flow in tempFlows:
+                if flow.get( 'state' ) != 'ADDED':
+                    main.log.info( self.name + ": flow Id: " +
+                                   str( flow.get( 'groupId' ) ) +
+                                   " | state:" +
+                                   str( flow.get( 'state' ) ) )
+                    returnValue = main.FALSE
+            return returnValue
+        except TypeError:
+            main.log.exception( self.name + ": Object not as expected" )
+            return main.FALSE
+        except Exception as e:
+            main.log.exception( e )
+            return None
+        except Exception:
+            main.log.exception( self.name + ": Uncaught exception!" )
+            main.cleanup()
+            main.exit()
diff --git a/TestON/tests/FUNCintent/Dependency/topo.py b/TestON/tests/FUNCintent/Dependency/topo.py
new file mode 100644
index 0000000..b44e3fc
--- /dev/null
+++ b/TestON/tests/FUNCintent/Dependency/topo.py
@@ -0,0 +1,100 @@
+"""
+    These functions can be used for topology comparisons
+"""
+
+import time
+import os
+import json
+
+def getAllDevices( main ):
+    """
+        Return a list containing the devices output from each ONOS node
+    """
+    devices = []
+    threads = []
+    for i in range( main.numCtrls ):
+        t = main.Thread( target=main.CLIs[i].devices,
+                         name="devices-" + str( i ),
+                         args=[ ] )
+        threads.append( t )
+        t.start()
+
+    for t in threads:
+        t.join()
+        devices.append( t.result )
+    return devices
+
+def getAllHosts( main ):
+    """
+        Return a list containing the hosts output from each ONOS node
+    """
+    hosts = []
+    ipResult = main.TRUE
+    threads = []
+    for i in range( main.numCtrls ):
+        t = main.Thread( target=main.CLIs[i].hosts,
+                         name="hosts-" + str( i ),
+                         args=[ ] )
+        threads.append( t )
+        t.start()
+
+    for t in threads:
+        t.join()
+        hosts.append( t.result )
+    return hosts
+
+def getAllPorts( main ):
+    """
+        Return a list containing the ports output from each ONOS node
+    """
+    ports = []
+    threads = []
+    for i in range( main.numCtrls ):
+        t = main.Thread( target=main.CLIs[i].ports,
+                         name="ports-" + str( i ),
+                         args=[ ] )
+        threads.append( t )
+        t.start()
+
+    for t in threads:
+        t.join()
+        ports.append( t.result )
+    return ports
+
+def getAllLinks( main ):
+    """
+        Return a list containing the links output from each ONOS node
+    """
+    links = []
+    threads = []
+    for i in range( main.numCtrls ):
+        t = main.Thread( target=main.CLIs[i].links,
+                         name="links-" + str( i ),
+                         args=[ ] )
+        threads.append( t )
+        t.start()
+
+    for t in threads:
+        t.join()
+        links.append( t.result )
+    return links
+
+def getAllClusters( main ):
+    """
+        Return a list containing the clusters output from each ONOS node
+    """
+    clusters = []
+    threads = []
+    for i in range( main.numCtrls ):
+        t = main.Thread( target=main.CLIs[i].clusters,
+                         name="clusters-" + str( i ),
+                         args=[ ] )
+        threads.append( t )
+        t.start()
+
+    for t in threads:
+        t.join()
+        clusters.append( t.result )
+    return clusters
+
+
diff --git a/TestON/tests/FUNCintent/FUNCintent.params b/TestON/tests/FUNCintent/FUNCintent.params
index 7b7040a..81e5bb2 100644
--- a/TestON/tests/FUNCintent/FUNCintent.params
+++ b/TestON/tests/FUNCintent/FUNCintent.params
@@ -2,6 +2,7 @@
     # CASE - Description
     # 1 - Variable initialization and optional pull and build ONOS package
     # 2 - Install ONOS
+    # 8 - Compare Topology
     # 9 - Report logs
     # 11 - Start Mininet
     # 12 - Assign switch to controller
@@ -12,7 +13,7 @@
     # 3000 - Test single to multi point intents
     # 4000 - Test multi to single point intents
 
-    <testcases>1,[2,10,12,13,1000,2000,3000,4000,14]*2,[2,11,12,13,1000,2000,3000,4000,14]*2</testcases>
+    <testcases>1,[2,10,12,8,13,1000,2000,3000,4000,5000,14]*2,[2,11,12,8,13,1000,2000,3000,4000,5000,14]*2</testcases>
 
     <SCALE>
         <size>1,3,1,3</size>
@@ -22,6 +23,7 @@
         <path>/tests/FUNCintent/Dependency/</path>
         <wrapper1>startUp</wrapper1>
         <wrapper2>FuncIntentFunction</wrapper2>
+        <wrapper3>topo</wrapper3>
         <topology>newFuncTopo.py</topology>
     </DEPENDENCY>
 
diff --git a/TestON/tests/FUNCintent/FUNCintent.py b/TestON/tests/FUNCintent/FUNCintent.py
index b1577ad..49c2969 100644
--- a/TestON/tests/FUNCintent/FUNCintent.py
+++ b/TestON/tests/FUNCintent/FUNCintent.py
@@ -32,55 +32,69 @@
         stepResult = main.FALSE
 
         # Test variables
-        main.testOnDirectory = os.path.dirname( os.getcwd ( ) )
-        main.apps = main.params[ 'ENV' ][ 'cellApps' ]
-        gitBranch = main.params[ 'GIT' ][ 'branch' ]
-        main.dependencyPath = main.testOnDirectory + \
-                              main.params[ 'DEPENDENCY' ][ 'path' ]
-        main.topology = main.params[ 'DEPENDENCY' ][ 'topology' ]
-        main.scale = ( main.params[ 'SCALE' ][ 'size' ] ).split( "," )
-        main.maxNodes = int( main.ONOSbench.maxNodes )
-        wrapperFile1 = main.params[ 'DEPENDENCY' ][ 'wrapper1' ]
-        wrapperFile2 = main.params[ 'DEPENDENCY' ][ 'wrapper2' ]
-        main.startUpSleep = int( main.params[ 'SLEEP' ][ 'startup' ] )
-        main.checkIntentSleep = int( main.params[ 'SLEEP' ][ 'checkintent' ] )
-        main.rerouteSleep = int( main.params[ 'SLEEP' ][ 'reroute' ] )
-        main.fwdSleep = int( main.params[ 'SLEEP' ][ 'fwd' ] )
-        gitPull = main.params[ 'GIT' ][ 'pull' ]
-        main.numSwitch = int( main.params[ 'MININET' ][ 'switch' ] )
-        main.numLinks = int( main.params[ 'MININET' ][ 'links' ] )
-        main.cellData = {} # for creating cell file
-        main.hostsData = {}
-        main.CLIs = []
-        main.ONOSip = []
+        try:
+            main.testOnDirectory = os.path.dirname( os.getcwd ( ) )
+            main.apps = main.params[ 'ENV' ][ 'cellApps' ]
+            gitBranch = main.params[ 'GIT' ][ 'branch' ]
+            main.dependencyPath = main.testOnDirectory + \
+                                  main.params[ 'DEPENDENCY' ][ 'path' ]
+            main.topology = main.params[ 'DEPENDENCY' ][ 'topology' ]
+            main.scale = ( main.params[ 'SCALE' ][ 'size' ] ).split( "," )
+            if main.ONOSbench.maxNodes:
+                main.maxNodes = int( main.ONOSbench.maxNodes )
+            else:
+                main.maxNodes = 0
+            wrapperFile1 = main.params[ 'DEPENDENCY' ][ 'wrapper1' ]
+            wrapperFile2 = main.params[ 'DEPENDENCY' ][ 'wrapper2' ]
+            wrapperFile3 = main.params[ 'DEPENDENCY' ][ 'wrapper3' ]
+            main.startUpSleep = int( main.params[ 'SLEEP' ][ 'startup' ] )
+            main.checkIntentSleep = int( main.params[ 'SLEEP' ][ 'checkintent' ] )
+            main.rerouteSleep = int( main.params[ 'SLEEP' ][ 'reroute' ] )
+            main.fwdSleep = int( main.params[ 'SLEEP' ][ 'fwd' ] )
+            gitPull = main.params[ 'GIT' ][ 'pull' ]
+            main.numSwitch = int( main.params[ 'MININET' ][ 'switch' ] )
+            main.numLinks = int( main.params[ 'MININET' ][ 'links' ] )
+            main.cellData = {} # for creating cell file
+            main.hostsData = {}
+            main.CLIs = []
+            main.ONOSip = []
 
-        main.ONOSip = main.ONOSbench.getOnosIps()
-        print main.ONOSip
+            main.ONOSip = main.ONOSbench.getOnosIps()
+            print main.ONOSip
 
-        # Assigning ONOS cli handles to a list
-        for i in range( 1,  main.maxNodes + 1 ):
-            main.CLIs.append( getattr( main, 'ONOScli' + str( i ) ) )
+            # Assigning ONOS cli handles to a list
+            for i in range( 1,  main.maxNodes + 1 ):
+                main.CLIs.append( getattr( main, 'ONOScli' + str( i ) ) )
 
-        # -- INIT SECTION, ONLY RUNS ONCE -- #
-        main.startUp = imp.load_source( wrapperFile1,
-                                        main.dependencyPath +
-                                        wrapperFile1 +
-                                        ".py" )
+            # -- INIT SECTION, ONLY RUNS ONCE -- #
+            main.startUp = imp.load_source( wrapperFile1,
+                                            main.dependencyPath +
+                                            wrapperFile1 +
+                                            ".py" )
 
-        main.intentFunction = imp.load_source( wrapperFile2,
-                                        main.dependencyPath +
-                                        wrapperFile2 +
-                                        ".py" )
+            main.intentFunction = imp.load_source( wrapperFile2,
+                                            main.dependencyPath +
+                                            wrapperFile2 +
+                                            ".py" )
 
-        copyResult = main.ONOSbench.copyMininetFile( main.topology,
-                                                     main.dependencyPath,
-                                                     main.Mininet1.user_name,
-                                                     main.Mininet1.ip_address )
-        if main.CLIs:
-            stepResult = main.TRUE
-        else:
-            main.log.error( "Did not properly created list of ONOS CLI handle" )
-            stepResult = main.FALSE
+            main.topo = imp.load_source( wrapperFile3,
+                                         main.dependencyPath +
+                                         wrapperFile3 +
+                                         ".py" )
+
+            copyResult = main.ONOSbench.copyMininetFile( main.topology,
+                                                         main.dependencyPath,
+                                                         main.Mininet1.user_name,
+                                                         main.Mininet1.ip_address )
+            if main.CLIs:
+                stepResult = main.TRUE
+            else:
+                main.log.error( "Did not properly created list of ONOS CLI handle" )
+                stepResult = main.FALSE
+        except Exception as e:
+            main.log.exception(e)
+            main.cleanup()
+            main.exit()
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
@@ -139,7 +153,9 @@
         for i in range( main.numCtrls ):
             tempOnosIp.append( main.ONOSip[i] )
 
-        main.ONOSbench.createCellFile( main.ONOSbench.ip_address, "temp", main.Mininet1.ip_address, main.apps, tempOnosIp )
+        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" )
@@ -222,6 +238,77 @@
         # Remove the first element in main.scale list
         main.scale.remove( main.scale[ 0 ] )
 
+    def CASE8( self, main ):
+        """
+        Compare Topo
+        """
+        import json
+
+        main.case( "Compare ONOS Topology view to Mininet topology" )
+        main.caseExplanation = "Compare topology elements between Mininet" +\
+                                " and ONOS"
+
+        main.step( "Gathering topology information" )
+        # TODO: add a paramaterized sleep here
+        devicesResults = main.TRUE
+        linksResults = main.TRUE
+        hostsResults = main.TRUE
+        devices = main.topo.getAllDevices( main )
+        hosts = main.topo.getAllHosts( main )
+        ports = main.topo.getAllPorts( main )
+        links = main.topo.getAllLinks( main )
+        clusters = main.topo.getAllClusters( main )
+
+        mnSwitches = main.Mininet1.getSwitches()
+        mnLinks = main.Mininet1.getLinks()
+        mnHosts = main.Mininet1.getHosts()
+
+        main.step( "Conmparing MN topology to ONOS topology" )
+        for controller in range( main.numCtrls ):
+            controllerStr = str( controller + 1 )
+            if devices[ controller ] and ports[ controller ] and\
+                "Error" not in devices[ controller ] and\
+                "Error" not in ports[ controller ]:
+
+                currentDevicesResult = main.Mininet1.compareSwitches(
+                        mnSwitches,
+                        json.loads( devices[ controller ] ),
+                        json.loads( ports[ controller ] ) )
+            else:
+                currentDevicesResult = main.FALSE
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=currentDevicesResult,
+                                     onpass="ONOS" + controllerStr +
+                                     " Switches view is correct",
+                                     onfail="ONOS" + controllerStr +
+                                     " Switches view is incorrect" )
+
+            if links[ controller ] and "Error" not in links[ controller ]:
+                currentLinksResult = main.Mininet1.compareLinks(
+                        mnSwitches, mnLinks,
+                        json.loads( links[ controller ] ) )
+            else:
+                currentLinksResult = main.FALSE
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=currentLinksResult,
+                                     onpass="ONOS" + controllerStr +
+                                     " links view is correct",
+                                     onfail="ONOS" + controllerStr +
+                                     " links view is incorrect" )
+
+            if hosts[ controller ] or "Error" not in hosts[ controller ]:
+                currentHostsResult = main.Mininet1.compareHosts(
+                        mnHosts,
+                        json.loads( hosts[ controller ] ) )
+            else:
+                currentHostsResult = main.FALSE
+            utilities.assert_equals( expect=main.TRUE,
+                                     actual=currentHostsResult,
+                                     onpass="ONOS" + controllerStr +
+                                     " hosts exist in Mininet",
+                                     onfail="ONOS" + controllerStr +
+                                     " hosts don't match Mininet" )
+
     def CASE9( self, main ):
         '''
             Report errors/warnings/exceptions
@@ -395,13 +482,14 @@
 
         intentLeadersOld = main.CLIs[ 0 ].leaderCandidates()
 
-        main.case( "TESTING HOST INTENTS" )
+        main.case( "Host Intents Test - " + str( main.numCtrls ) +
+                   " NODE(S) - OF " + main.OFProtocol )
         main.caseExplanation = "This test case tests Host intents using " +\
                                 str( main.numCtrls ) + " node(s) cluster;\n" +\
                                 "Different type of hosts will be tested in " +\
                                 "each step such as IPV4, Dual stack, VLAN " +\
                                 "etc;\nThe test will use OF " + main.OFProtocol\
-                                + "OVS running in Mininet"
+                                + " OVS running in Mininet"
 
         main.step( "IPV4: Add host intents between h1 and h9" )
         stepResult = main.TRUE
@@ -418,8 +506,10 @@
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="IPV4: Add host intent successful",
-                                 onfail="IPV4: Add host intent failed" )
+                                 onpass="IPV4: Host intent test successful " +
+                                        "between two IPV4 hosts",
+                                 onfail="IPV4: Host intent test failed " +
+                                        "between two IPV4 hosts")
 
         main.step( "DUALSTACK1: Add host intents between h3 and h11" )
         stepResult = main.TRUE
@@ -435,9 +525,13 @@
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="DUALSTACK1: Add host intent" +
-                                        " successful",
-                                 onfail="DUALSTACK1: Add host intent failed" )
+                                 onpass="DUALSTACK: Host intent test " +
+                                        "successful between two " +
+                                        "dual stack host using IPV4",
+                                 onfail="DUALSTACK: Host intent test " +
+                                        "failed between two" +
+                                        "dual stack host using IPV4" )
+
 
         main.step( "DUALSTACK2: Add host intents between h1 and h11" )
         stepResult = main.TRUE
@@ -451,9 +545,12 @@
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="DUALSTACK2: Add host intent" +
-                                        " successful",
-                                 onfail="DUALSTACK2: Add host intent failed" )
+                                 onpass="DUALSTACK2: Host intent test " +
+                                        "successful between two " +
+                                        "dual stack host using IPV4",
+                                 onfail="DUALSTACK2: Host intent test " +
+                                        "failed between two" +
+                                        "dual stack host using IPV4" )
 
         main.step( "1HOP: Add host intents between h1 and h3" )
         stepResult = main.TRUE
@@ -464,9 +561,12 @@
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="1HOP: Add host intent" +
-                                        " successful",
-                                 onfail="1HOP: Add host intent failed" )
+                                 onpass="1HOP: Host intent test " +
+                                        "successful between two " +
+                                        "host using IPV4 in the same switch",
+                                 onfail="1HOP: Host intent test " +
+                                        "failed between two" +
+                                        "host using IPV4 in the same switch" )
 
         main.step( "VLAN1: Add vlan host intents between h4 and h12" )
         stepResult = main.TRUE
@@ -482,9 +582,12 @@
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="VLAN1: Add vlan host" +
-                                        " intent successful",
-                                 onfail="VLAN1: Add vlan host intent failed" )
+                                 onpass="VLAN1: Host intent test " +
+                                        "successful between two " +
+                                        "host using IPV4 in the same VLAN",
+                                 onfail="VLAN1: Host intent test " +
+                                        "failed between two" +
+                                        "host using IPV4 in the same VLAN" )
 
         main.step( "VLAN2: Add inter vlan host intents between h13 and h20" )
         stepResult = main.TRUE
@@ -495,10 +598,12 @@
 
         utilities.assert_equals( expect=main.FALSE,
                                  actual=stepResult,
-                                 onpass="VLAN2: Add inter vlan host" +
-                                        " intent successful",
-                                 onfail="VLAN2: Add inter vlan host" +
-                                        " intent failed" )
+                                 onpass="VLAN2: Host intent negative test " +
+                                        "successful between two " +
+                                        "host using IPV4 in different VLAN",
+                                 onfail="VLAN2: Host intent negative test " +
+                                        "failed between two" +
+                                        "host using IPV4 in different VLAN" )
 
 
         intentLeadersNew = main.CLIs[ 0 ].leaderCandidates()
@@ -536,14 +641,15 @@
         assert main.numSwitch, "Placed the total number of switch topology in \
                                 main.numSwitch"
 
-        main.case( "TESTING POINT INTENTS" )
+        main.case( "Point Intents Test - " + str( main.numCtrls ) +
+                   " NODE(S) - OF " + main.OFProtocol )
         main.caseExplanation = "This test case will test point to point" +\
                                " intents using " + str( main.numCtrls ) +\
                                " node(s) cluster;\n" +\
                                "Different type of hosts will be tested in " +\
                                "each step such as IPV4, Dual stack, VLAN etc" +\
                                ";\nThe test will use OF " + main.OFProtocol +\
-                               "OVS running in Mininet"
+                               " OVS running in Mininet"
 
         # No option point intents
         main.step( "NOOPTION: Add point intents between h1 and h9" )
@@ -561,8 +667,10 @@
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="NOOPTION: Add point intent successful",
-                                 onfail="NOOPTION: Add point intent failed" )
+                                 onpass="NOOPTION: Point intent test " +
+                                        "successful using no match action",
+                                 onfail="NOOPTION: Point intent test " +
+                                        "failed using no match action" )
 
         stepResult = main.TRUE
         main.step( "IPV4: Add point intents between h1 and h9" )
@@ -591,9 +699,12 @@
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="IPV4: Add point intent successful",
-                                 onfail="IPV4: Add point intent failed" )
-
+                                 onpass="IPV4: Point intent test " +
+                                        "successful using IPV4 type with " +
+                                        "MAC addresses",
+                                 onfail="IPV4: Point intent test " +
+                                        "failed using IPV4 type with " +
+                                        "MAC addresses" )
         main.step( "IPV4_2: Add point intents between h1 and h9" )
         stepResult = main.TRUE
         stepResult = main.intentFunction.pointIntent(
@@ -614,8 +725,12 @@
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="IPV4_2: Add point intent successful",
-                                 onfail="IPV4_2: Add point intent failed" )
+                                 onpass="IPV4_2: Point intent test " +
+                                        "successful using IPV4 type with " +
+                                        "no MAC addresses",
+                                 onfail="IPV4_2: Point intent test " +
+                                        "failed using IPV4 type with " +
+                                        "no MAC addresses" )
 
         main.step( "SDNIP-TCP: Add point intents between h1 and h9" )
         stepResult = main.TRUE
@@ -631,6 +746,7 @@
             ip2 = main.Mininet1.getIPAddress( 'h9')
 
         ipProto = main.params[ 'SDNIP' ][ 'icmpProto' ]
+        # Uneccessary, not including this in the selectors
         tcp1 = main.params[ 'SDNIP' ][ 'srcPort' ]
         tcp2 = main.params[ 'SDNIP' ][ 'dstPort' ]
 
@@ -646,14 +762,16 @@
                                            ethType="IPV4",
                                            ipProto=ipProto,
                                            ip1=ip1,
-                                           ip2=ip2,
-                                           tcp1=tcp1,
-                                           tcp2=tcp2 )
+                                           ip2=ip2 )
 
         utilities.assert_equals( expect=main.TRUE,
-                             actual=stepResult,
-                             onpass="SDNIP-TCP: Add point intent successful",
-                             onfail="SDNIP-TCP: Add point intent failed" )
+                                 actual=stepResult,
+                                 onpass="SDNIP-TCP: Point intent test " +
+                                        "successful using IPV4 type with " +
+                                        "IP protocol TCP enabled",
+                                 onfail="SDNIP-TCP: Point intent test " +
+                                        "failed using IPV4 type with " +
+                                        "IP protocol TCP enabled" )
 
         main.step( "SDNIP-ICMP: Add point intents between h1 and h9" )
         stepResult = main.TRUE
@@ -679,8 +797,12 @@
 
         utilities.assert_equals( expect=main.TRUE,
                              actual=stepResult,
-                             onpass="SDNIP-ICMP: Add point intent successful",
-                             onfail="SDNIP-ICMP: Add point intent failed" )
+                                 onpass="SDNIP-ICMP: Point intent test " +
+                                        "successful using IPV4 type with " +
+                                        "IP protocol ICMP enabled",
+                                 onfail="SDNIP-ICMP: Point intent test " +
+                                        "failed using IPV4 type with " +
+                                        "IP protocol ICMP enabled" )
 
         main.step( "DUALSTACK1: Add point intents between h1 and h9" )
         stepResult = main.TRUE
@@ -709,9 +831,12 @@
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="DUALSTACK1: Add point intent" +
-                                        " successful",
-                                 onfail="DUALSTACK1: Add point intent failed" )
+                                 onpass="DUALSTACK1: Point intent test " +
+                                        "successful using IPV4 type with " +
+                                        "MAC addresses",
+                                 onfail="DUALSTACK1: Point intent test " +
+                                        "failed using IPV4 type with " +
+                                        "MAC addresses" )
 
         main.step( "VLAN: Add point intents between h5 and h21" )
         stepResult = main.TRUE
@@ -740,8 +865,12 @@
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="VLAN: Add point intent successful",
-                                 onfail="VLAN: Add point intent failed" )
+                                 onpass="VLAN1: Point intent test " +
+                                        "successful using IPV4 type with " +
+                                        "MAC addresses",
+                                 onfail="VLAN1: Point intent test " +
+                                        "failed using IPV4 type with " +
+                                        "MAC addresses" )
 
         main.step( "1HOP: Add point intents between h1 and h3" )
         stepResult = main.TRUE
@@ -752,9 +881,12 @@
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="1HOP: Add point intent" +
-                                        " successful",
-                                 onfail="1HOP: Add point intent failed" )
+                                 onpass="1HOP: Point intent test " +
+                                        "successful using IPV4 type with " +
+                                        "no MAC addresses",
+                                 onfail="1HOP: Point intent test " +
+                                        "failed using IPV4 type with " +
+                                        "no MAC addresses" )
 
     def CASE3000( self, main ):
         """
@@ -781,14 +913,15 @@
         assert main.numSwitch, "Placed the total number of switch topology in \
                                 main.numSwitch"
 
-        main.case( "TESTING SINGLE TO MULTI POINT INTENTS" )
+        main.case( "Single To Multi Point Intents Test - " +
+                   str( main.numCtrls ) + " NODE(S) - OF " + main.OFProtocol )
         main.caseExplanation = "This test case will test single point to" +\
                                " multi point intents using " +\
                                str( main.numCtrls ) + " node(s) cluster;\n" +\
                                "Different type of hosts will be tested in " +\
                                "each step such as IPV4, Dual stack, VLAN etc" +\
                                ";\nThe test will use OF " + main.OFProtocol +\
-                               "OVS running in Mininet"
+                               " OVS running in Mininet"
 
         main.step( "NOOPTION: Add single point to multi point intents" )
         stepResult = main.TRUE
@@ -808,9 +941,11 @@
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
                                  onpass="NOOPTION: Successfully added single "
-                                        + " point to multi point intents",
-                                 onfail="NOOPTION: Failed to add single point" +
-                                        " to multi point intents" )
+                                        + " point to multi point intents" +
+                                        " with no match action",
+                                 onfail="NOOPTION: Failed to add single point"
+                                        + " point to multi point intents" +
+                                        " with no match action" )
 
         main.step( "IPV4: Add single point to multi point intents" )
         stepResult = main.TRUE
@@ -833,10 +968,12 @@
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="IPV4: Successfully added single point"
-                                        + " to multi point intents",
-                                 onfail="IPV4: Failed to add single point" +
-                                        " to multi point intents" )
+                                 onpass="IPV4: Successfully added single "
+                                        + " point to multi point intents" +
+                                        " with IPV4 type and MAC addresses",
+                                 onfail="IPV4: Failed to add single point"
+                                        + " point to multi point intents" +
+                                        " with IPV4 type and MAC addresses" )
 
         main.step( "IPV4_2: Add single point to multi point intents" )
         stepResult = main.TRUE
@@ -851,9 +988,11 @@
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
                                  onpass="IPV4_2: Successfully added single "
-                                        + " point to multi point intents",
-                                 onfail="IPV4_2: Failed to add single point" +
-                                        " to multi point intents" )
+                                        + " point to multi point intents" +
+                                        " with IPV4 type and no MAC addresses",
+                                 onfail="IPV4_2: Failed to add single point"
+                                        + " point to multi point intents" +
+                                        " with IPV4 type and no MAC addresses" )
 
         main.step( "VLAN: Add single point to multi point intents" )
         stepResult = main.TRUE
@@ -880,10 +1019,14 @@
 
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
-                                 onpass="VLAN: Successfully added single point"
-                                        + " to multi point intents",
-                                 onfail="VLAN: Failed to add single point" +
-                                        " to multi point intents" )
+                                 onpass="VLAN: Successfully added single "
+                                        + " point to multi point intents" +
+                                        " with IPV4 type and MAC addresses" +
+                                        " in the same VLAN",
+                                 onfail="VLAN: Failed to add single point"
+                                        + " point to multi point intents" +
+                                        " with IPV4 type and MAC addresses" +
+                                        " in the same VLAN")
 
     def CASE4000( self, main ):
         """
@@ -910,14 +1053,15 @@
         assert main.numSwitch, "Placed the total number of switch topology in \
                                 main.numSwitch"
 
-        main.case( "TESTING MULTI TO SINGLE POINT INTENTS" )
+        main.case( "Multi To Single Point Intents Test - " +
+                   str( main.numCtrls ) + " NODE(S) - OF " + main.OFProtocol )
         main.caseExplanation = "This test case will test single point to" +\
                                " multi point intents using " +\
                                str( main.numCtrls ) + " node(s) cluster;\n" +\
                                "Different type of hosts will be tested in " +\
                                "each step such as IPV4, Dual stack, VLAN etc" +\
                                ";\nThe test will use OF " + main.OFProtocol +\
-                               "OVS running in Mininet"
+                               " OVS running in Mininet"
 
         main.step( "NOOPTION: Add multi point to single point intents" )
         stepResult = main.TRUE
@@ -937,9 +1081,11 @@
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
                                  onpass="NOOPTION: Successfully added multi "
-                                        + " point to single point intents",
+                                        + " point to single point intents" +
+                                        " with no match action",
                                  onfail="NOOPTION: Failed to add multi point" +
-                                        " to single point intents" )
+                                        " to single point intents" +
+                                        " with no match action" )
 
         main.step( "IPV4: Add multi point to single point intents" )
         stepResult = main.TRUE
@@ -963,9 +1109,11 @@
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
                                  onpass="IPV4: Successfully added multi point"
-                                        + " to single point intents",
+                                        + " to single point intents" +
+                                        " with IPV4 type and MAC addresses",
                                  onfail="IPV4: Failed to add multi point" +
-                                        " to single point intents" )
+                                        " to single point intents" +
+                                        " with IPV4 type and MAC addresses" )
 
         main.step( "IPV4_2: Add multi point to single point intents" )
         stepResult = main.TRUE
@@ -980,9 +1128,11 @@
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
                                  onpass="IPV4_2: Successfully added multi point"
-                                        + " to single point intents",
+                                        + " to single point intents" +
+                                        " with IPV4 type and no MAC addresses",
                                  onfail="IPV4_2: Failed to add multi point" +
-                                        " to single point intents" )
+                                        " to single point intents" +
+                                        " with IPV4 type and no MAC addresses" )
 
         main.step( "VLAN: Add multi point to single point intents" )
         stepResult = main.TRUE
@@ -1010,6 +1160,53 @@
         utilities.assert_equals( expect=main.TRUE,
                                  actual=stepResult,
                                  onpass="VLAN: Successfully added multi point"
-                                        + " to single point intents",
+                                        + " to single point intents" +
+                                        " with IPV4 type and MAC addresses" +
+                                        " in the same VLAN",
                                  onfail="VLAN: Failed to add multi point" +
                                         " to single point intents" )
+
+    def CASE5000( self, main ):
+        """
+        Will add description in next patch set
+        """
+        assert main, "There is no main"
+        assert main.CLIs, "There is no main.CLIs"
+        assert main.Mininet1, "Mininet handle should be named Mininet1"
+        assert main.numSwitch, "Placed the total number of switch topology in \
+                                main.numSwitch"
+        main.case( "Test host mobility with host intents " )
+        main.step( " Testing host mobility by moving h1 from s5 to s6" )
+        h1PreMove = main.hostsData[ "h1" ][ "location" ][ 0:19 ]
+
+        main.log.info( "Moving h1 from s5 to s6")
+
+        main.Mininet1.moveHost( "h1","s5","s6" )
+
+        main.intentFunction.getHostsData( main )
+        h1PostMove = main.hostsData[ "h1" ][ "location" ][ 0:19 ]
+
+        utilities.assert_equals( expect="of:0000000000000006",
+                                 actual=h1PostMove,
+                                 onpass="Mobility: Successfully moved h1 to s6",
+                                 onfail="Mobility: Failed to moved h1 to s6" +
+                                        " to single point intents" +
+                                        " with IPV4 type and MAC addresses" +
+                                        " in the same VLAN" )
+
+        main.step( "IPV4: Add host intents between h1 and h9" )
+        stepResult = main.TRUE
+        stepResult = main.intentFunction.hostIntent( main,
+                                              onosNode='0',
+                                              name='IPV4',
+                                              host1='h1',
+                                              host2='h9',
+                                              host1Id='00:00:00:00:00:01/-1',
+                                              host2Id='00:00:00:00:00:09/-1' )
+
+        utilities.assert_equals( expect=main.TRUE,
+                                 actual=stepResult,
+                                 onpass="IPV4: Host intent test successful " +
+                                        "between two IPV4 hosts",
+                                 onfail="IPV4: Host intent test failed " +
+                                        "between two IPV4 hosts")
diff --git a/TestON/tests/SCPFportLat/SCPFportLat.params b/TestON/tests/SCPFportLat/SCPFportLat.params
new file mode 100644
index 0000000..a453767
--- /dev/null
+++ b/TestON/tests/SCPFportLat/SCPFportLat.params
@@ -0,0 +1,86 @@
+<PARAMS>
+    <testcases>1,2,1,2,1,2,1,2</testcases>
+
+    <SCALE>1,3,5,7</SCALE>
+    <max>7</max>
+
+    <ENV>
+        <cellName>topo_perf_test</cellName>
+        <cellApps>drivers,metrics,openflow</cellApps>
+    </ENV>
+
+    <GIT>
+        <autopull>off</autopull>
+        <checkout>master</checkout>
+    </GIT>
+
+    <CTRL>
+        <user>admin</user>
+        <ip1>OC1</ip1>
+        <ip2>OC2</ip2>
+        <ip3>OC3</ip3>
+        <ip4>OC4</ip4>
+        <ip5>OC5</ip5>
+        <ip6>OC6</ip6>
+        <ip7>OC7</ip7>
+    </CTRL>
+
+    <MN>
+        <ip1>localhost</ip1>
+    </MN>
+
+    <BENCH>
+        <ip>localhost</ip>
+    </BENCH>
+
+    <TSHARK>
+        <ofpPortStatus>OF 1.3 146</ofpPortStatus>
+        <ofpRoleReply>OF 1.3 90 of_role_reply</ofpRoleReply>
+        <featureReply>OF 1.3 98 of_features_reply</featureReply>
+        <roleRequest>OF 1.3 90 of_role_request</roleRequest>
+        <tcpSynAck>TCP 74 6633</tcpSynAck>
+        <finAckSequence>FIN</finAckSequence>
+    </TSHARK>
+
+    <TEST>
+        #'on' or 'off' debug mode.
+        #If on, logging will be more verbose and
+        #tshark pcap will be enabled
+        #pcap file located at /tmp/'capture_name'
+        <debugMode>on</debugMode>
+        <onosLogFile>/opt/onos/log/karaf*</onosLogFile>
+        <mci>off</mci>
+
+        <topoConfigFile>
+            single_topo_event_accumulator.cfg
+        </topoConfigFile>
+        <topoConfigName>
+            org.onlab.onos.net.topology.impl.DefaultTopologyProvider.cfg
+        </topoConfigName>
+
+        #Number of times to iterate each case
+        <numIter>2</numIter>
+        <numSwitch>2</numSwitch>
+        #Number of iterations to ignore initially
+        <iterIgnore>0</iterIgnore>
+
+        <portUpThreshold>0,1000</portUpThreshold>
+        <portDownThreshold>0,1000</portDownThreshold>
+
+        <tabletFile>tablets_3node.json</tabletFile>
+   </TEST>
+
+    <DB>
+        <postToDB>no</postToDB>
+        <portEventResultPath>
+            /tmp/portEventResultDb.log
+        </portEventResultPath>
+    </DB>
+
+    <JSON>
+        <deviceTimestamp>topologyDeviceEventTimestamp</deviceTimestamp>
+        <hostTimestamp>topologyHostEventTimestamp</hostTimestamp>
+        <linkTimestamp>topologyLinkEventTimestamp</linkTimestamp>
+        <graphTimestamp>topologyGraphEventTimestamp</graphTimestamp>
+    </JSON>
+</PARAMS>
diff --git a/TestON/tests/SCPFportLat/SCPFportLat.py b/TestON/tests/SCPFportLat/SCPFportLat.py
new file mode 100644
index 0000000..9c75cb8
--- /dev/null
+++ b/TestON/tests/SCPFportLat/SCPFportLat.py
@@ -0,0 +1,552 @@
+# CASE1 starts number of nodes specified in param file
+#
+# cameron@onlab.us
+
+class SCPFportLat:
+
+    def __init__( self ):
+        self.default = ''
+
+    def CASE1( self, main ):
+        import sys
+        import os.path
+        import os
+        import time
+
+        global init
+        try:
+            if type(init) is not bool:
+                init = Fals
+        except NameError:
+            init = False
+
+        #Load values from params file
+        checkoutBranch = main.params[ 'GIT' ][ 'checkout' ]
+        gitPull = main.params[ 'GIT' ][ 'autopull' ]
+        cellName = main.params[ 'ENV' ][ 'cellName' ]
+        Apps = main.params[ 'ENV' ][ 'cellApps' ]
+        BENCHIp = main.params[ 'BENCH' ][ 'ip' ]
+        MN1Ip = main.params[ 'MN' ][ 'ip1' ]
+        main.maxNodes = int(main.params[ 'max' ])
+        cellName = main.params[ 'ENV' ][ 'cellName' ]
+        homeDir = os.path.expanduser('~')
+        topoCfgFile = main.params['TEST']['topoConfigFile']
+        topoCfgName = main.params['TEST']['topoConfigName']
+        portEventResultPath = main.params['DB']['portEventResultPath']
+        skipMvn = main.params ['TEST']['mci']
+        testONpath = os.getcwd() #testON/bin
+
+        # -- INIT SECTION, ONLY RUNS ONCE -- #
+        if init == False:
+            init = True
+            global clusterCount             #number of nodes running
+            global ONOSIp                   #list of ONOS IP addresses
+            global scale
+            global commit
+            global timeToPost
+            global runNum
+            global jenkinsBuildNumber
+            global CLIs
+            CLIs = []
+
+            timeToPost = time.strftime('%Y-%m-%d %H:%M:%S')
+            runNum = time.strftime('%d%H%M%S')
+            ONOSIp = main.ONOSbench.getOnosIps()
+
+            #Assigning ONOS cli handles to a list
+            for i in range(main.maxNodes):
+                CLIs.append( getattr( main, 'ONOS' + str(i+1) + 'cli'))
+
+            try:
+                jenkinsBuildNumber = str(os.environ['BUILD_NUMBER'])
+                main.log.report( 'Jenkins build number: ' + jenkinsBuildNumber )
+            except KeyError:
+                jenkinsBuildNumber = str(0)
+                main.log.info( 'Job is not run by jenkins. ' + 'Build number set to: ' + jenkinsBuildNumber)
+
+            clusterCount = 0
+            ONOSIp = main.ONOSbench.getOnosIps()
+
+            scale = (main.params[ 'SCALE' ]).split(",")
+            clusterCount = int(scale[0])
+
+            #mvn clean install, for debugging set param 'skipCleanInstall' to yes to speed up test
+            if skipMvn != "off":
+                mvnResult = main.ONOSbench.cleanInstall()
+
+            #git
+            main.step( "Git checkout and pull " + checkoutBranch )
+            if gitPull == 'on':
+                checkoutResult = main.ONOSbench.gitCheckout( checkoutBranch )
+                pullResult = main.ONOSbench.gitPull()
+
+            else:
+                checkoutResult = main.TRUE
+                pullResult = main.TRUE
+                main.log.info( "Skipped git checkout and pull" )
+
+            main.step("Grabbing commit number")
+            commit = main.ONOSbench.getVersion()
+            commit = (commit.split(" "))[1]
+
+            main.step("Creating results file")
+            resultsDB = open("IntentEventTPDB", "w+")
+            resultsDB.close()
+
+            main.log.report('Commit information - ')
+            main.ONOSbench.getVersion(report=True)
+
+        # -- END OF INIT SECTION --#
+
+        main.step("Adjusting scale")
+        clusterCount = int(scale[0])
+        scale.remove(scale[0])
+
+        #kill off all onos processes
+        main.step("Killing all ONOS processes before environmnet setup")
+        for node in range(main.maxNodes):
+            main.ONOSbench.onosDie(ONOSIp[node])
+
+        #Uninstall everywhere
+        main.step( "Cleaning Enviornment..." )
+        for i in range(main.maxNodes):
+            main.log.info(" Uninstalling ONOS " + str(i) )
+            main.ONOSbench.onosUninstall( ONOSIp[i] )
+        main.log.info("Sleep 10 second for uninstall to settle...")
+        time.sleep(10)
+        main.ONOSbench.handle.sendline(" ")
+        main.ONOSbench.handle.expect(":~")
+
+        #construct the cell file
+        main.log.info("Creating cell file")
+        cellIp = []
+        for node in range (clusterCount):
+            cellIp.append(ONOSIp[node])
+
+        main.ONOSbench.createCellFile("localhost",cellName,MN1Ip,str(Apps), cellIp)
+
+        main.step( "Set Cell" )
+        main.ONOSbench.setCell(cellName)
+
+        main.step( "Creating ONOS package" )
+        packageResult = main.ONOSbench.onosPackage()
+
+        main.step( "verify cells" )
+        verifyCellResult = main.ONOSbench.verifyCell()
+
+        main.step('Starting mininet topology ')
+        main.Mininet1.startNet()
+
+        main.step('Clearing previous DB log file')
+        fPortLog = open(portEventResultPath, 'w+')
+        fPortLog.close()
+
+        main.log.report( "Initializeing " + str( clusterCount ) + " node cluster." )
+        for node in range(clusterCount):
+            main.log.info("Starting ONOS " + str(node) + " at IP: " + ONOSIp[node])
+            main.ONOSbench.onosInstall( ONOSIp[node])
+
+        for node in range(clusterCount):
+            for i in range( 2 ):
+                isup = main.ONOSbench.isup( ONOSIp[node] )
+                if isup:
+                    main.log.info("ONOS " + str(node + 1) + " is up\n")
+                    break
+            if not isup:
+                main.log.report( "ONOS " + str(node) + " didn't start!" )
+        main.log.info("Startup sequence complete")
+
+        main.step('Starting onos CLIs')
+        for i in range(clusterCount):
+            CLIs[i].startOnosCli(ONOSIp[i])
+
+        time.sleep(20)
+
+        main.step( 'activating essential applications' )
+        CLIs[0].activateApp( 'org.onosproject.metrics' )
+        CLIs[0].activateApp( 'org.onosproject.openflow' )
+
+        main.step( 'Configuring application parameters' )
+
+        configName = 'org.onosproject.net.topology.impl.DefaultTopologyProvider'
+        configParam = 'maxEvents 1'
+        main.ONOSbench.onosCfgSet( ONOSIp[0], configName, configParam )
+        configParam = 'maxBatchMs 0'
+        main.ONOSbench.onosCfgSet( ONOSIp[0], configName, configParam )
+        configParam = 'maxIdleMs 0'
+        main.ONOSbench.onosCfgSet( ONOSIp[0], configName, configParam )
+
+    def CASE2( self, main ):
+        """
+        Bring port up / down and measure latency.
+        Port enable / disable is simulated by ifconfig up / down
+
+        In ONOS-next, we must ensure that the port we are
+        manipulating is connected to another switch with a valid
+        connection. Otherwise, graph view will not be updated.
+        """
+        import time
+        import subprocess
+        import os
+        import requests
+        import json
+        import numpy
+
+        ONOSUser = main.params['CTRL']['user']
+        numIter = main.params['TEST']['numIter']
+        iterIgnore = int(main.params['TEST']['iterIgnore'])
+
+        deviceTimestampKey = main.params['JSON']['deviceTimestamp']
+        graphTimestampKey = main.params['JSON']['graphTimestamp']
+        linkTimestampKey = main.params['JSON']['linkTimestamp']
+
+        tsharkPortUp = '/tmp/tshark_port_up.txt'
+        tsharkPortDown = '/tmp/tshark_port_down.txt'
+        tsharkPortStatus = main.params[ 'TSHARK' ][ 'ofpPortStatus' ]
+
+        debugMode = main.params['TEST']['debugMode']
+        postToDB = main.params['DB']['postToDB']
+        resultPath = main.params['DB']['portEventResultPath']
+        localTime = time.strftime('%x %X')
+        localTime = localTime.replace('/', '')
+        localTime = localTime.replace(' ', '_')
+        localTime = localTime.replace(':', '')
+
+        if debugMode == 'on':
+            main.ONOSbench.tsharkPcap('eth0', '/tmp/port_lat_pcap_' + localTime)
+
+        upThresholdStr = main.params['TEST']['portUpThreshold']
+        downThresholdStr = main.params['TEST']['portDownThreshold']
+        upThresholdObj = upThresholdStr.split(',')
+        downThresholdObj = downThresholdStr.split(',')
+        upThresholdMin = int(upThresholdObj[0])
+        upThresholdMax = int(upThresholdObj[1])
+        downThresholdMin = int(downThresholdObj[0])
+        downThresholdMax = int(downThresholdObj[1])
+
+        interfaceConfig = 's1-eth1'
+        main.log.report('Port enable / disable latency')
+        main.log.report('Simulated by ifconfig up / down')
+        main.log.report('Total iterations of test: ' + str(numIter))
+        main.step('Assign switches s1 and s2 to controller 1')
+
+        main.Mininet1.assignSwController(sw='s1', ip=ONOSIp[0])
+        main.Mininet1.assignSwController(sw='s2', ip=ONOSIp[0])
+
+        time.sleep(15)
+
+        portUpEndToEndNodeIter = numpy.zeros((clusterCount, int(numIter)))
+        portUpOfpToDevNodeIter = numpy.zeros((clusterCount, int(numIter)))
+        portUpDevToLinkNodeIter = numpy.zeros((clusterCount, int(numIter)))
+        portUpLinkToGraphNodeIter = numpy.zeros((clusterCount, int(numIter)))
+
+        portDownEndToEndNodeIter = numpy.zeros((clusterCount, int(numIter)))
+        portDownOfpToDevNodeIter = numpy.zeros((clusterCount, int(numIter)))
+        portDownDevToLinkNodeIter = numpy.zeros((clusterCount, int(numIter)))
+        portDownLinkToGraphNodeIter = numpy.zeros((clusterCount, int(numIter)))
+
+        for i in range(0, int(numIter)):
+            main.step('Starting wireshark capture for port status down')
+            main.ONOSbench.tsharkGrep(tsharkPortStatus, tsharkPortDown)
+
+            time.sleep(2)
+
+            main.step('Disable port: ' + interfaceConfig)
+            main.Mininet1.handle.sendline('sh ifconfig ' +
+                    interfaceConfig + ' down')
+            main.Mininet1.handle.expect('mininet>')
+
+            time.sleep(2)
+
+            jsonStrPtDown = []
+            for node in range (0, clusterCount):
+                metricsPortDown = CLIs[node].topologyEventsMetrics()
+                jsonStrPtDown.append(metricsPortDown)
+
+            time.sleep(10)
+
+            main.ONOSbench.tsharkStop()
+
+            fPortDown = open(tsharkPortDown, 'r')
+            fLine = fPortDown.readline()
+            objDown = fLine.split(' ')
+            if len(fLine) > 0:
+                timestampBeginPtDown = int(float(objDown[1]) * 1000)
+                # At times, tshark reports timestamp at the 3rd
+                # index of the array. If initial readings were
+                # unlike the epoch timestamp, then check the 3rd
+                # index and set that as a timestamp
+                if timestampBeginPtDown < 1400000000000:
+                   timestampBeginPtDown = int(float(objDown[2]) * 1000)
+            else:
+                main.log.info('Tshark output file returned unexpected' +
+                        ' results: ' + str(objDown))
+                timestampBeginPtDown = 0
+            fPortDown.close()
+
+            for node in range(0, clusterCount):
+                nodeNum = node+1
+                metricsDown = CLIs[node].topologyEventsMetrics
+                jsonStrPtDown[node] = metricsDown()
+                jsonObj = json.loads(jsonStrPtDown[node])
+
+                if jsonObj:
+                    graphTimestamp = jsonObj[graphTimestampKey]['value']
+                    deviceTimestamp = jsonObj[deviceTimestampKey]['value']
+                    linkTimestamp = jsonObj[linkTimestampKey]['value']
+                else:
+                    main.log.error( "Unexpected json object" )
+                    graphTimestamp = 0
+                    deviceTimestamp = 0
+                    linkTimestamp = 0
+
+                main.log.info('ptDownTimestamp: ' + str(timestampBeginPtDown))
+                main.log.info("graphTimestamp: " + str(graphTimestamp))
+                main.log.info("deviceTimestamp: " + str(deviceTimestamp))
+                main.log.info("linkTimestamp: " + str(linkTimestamp))
+
+                ptDownEndToEnd = int(graphTimestamp) - int(timestampBeginPtDown)
+                ptDownOfpToDevice = float(deviceTimestamp) - float(timestampBeginPtDown)
+                ptDownDeviceToLink = float(linkTimestamp) - float(deviceTimestamp)
+                ptDownLinkToGraph = float(graphTimestamp) - float(linkTimestamp)
+
+                if ptDownEndToEnd < downThresholdMin or ptDownEndToEnd >= downThresholdMax:
+                    main.log.info("ONOS " +str(nodeNum) + " surpassed threshold - port down End-to-end: "+ str(ptDownEndToEnd) + " ms")
+                elif i < iterIgnore:
+                    main.log.info("ONOS "+str(nodeNum) + " warming up - port down End-to-end: "+ str(ptDownEndToEnd) + " ms")
+                else:
+                    portDownEndToEndNodeIter[node][i] = ptDownEndToEnd
+                    main.log.info("ONOS "+str(nodeNum) + " port down End-to-end: "+ str(ptDownEndToEnd) + " ms")
+
+                if ptDownOfpToDevice < downThresholdMin or ptDownOfpToDevice >= downThresholdMax:
+                    main.log.info("ONOS " +str(nodeNum) + " surpassed threshold - port down Ofp-to-device: "+ str(ptDownOfpToDevice) + " ms")
+                elif i < iterIgnore:
+                    main.log.info("ONOS "+str(nodeNum) + " warming up - port down Ofp-to-device: "+ str(ptDownOfpToDevice) + " ms")
+                else:
+                    portDownOfpToDevNodeIter[node][i] = ptDownOfpToDevice
+                    main.log.info("ONOS "+str(nodeNum) + " port down Ofp-to-device: "+ str(ptDownOfpToDevice) + " ms")
+
+                if ptDownDeviceToLink < downThresholdMin or ptDownDeviceToLink >= downThresholdMax:
+                    main.log.info("ONOS " +str(nodeNum) + " surpassed threshold - port down Device-to-link: "+ str(ptDownDeviceToLink) + " ms")
+                elif i < iterIgnore:
+                    main.log.info("ONOS "+str(nodeNum) + " warming up - port down Device-to-link: "+ str(ptDownDeviceToLink) + " ms")
+                else:
+                    portDownDevToLinkNodeIter[node][i] = ptDownDeviceToLink
+                    main.log.info("ONOS "+str(nodeNum) + " port down Device-to-link: "+ str(ptDownDeviceToLink) + " ms")
+
+                if ptDownLinkToGraph < downThresholdMin or ptDownLinkToGraph >= downThresholdMax:
+                    main.log.info("ONOS " +str(nodeNum) + " surpassed threshold - port down Link-to-graph: "+ str(ptDownLinkToGraph) + " ms")
+                elif i < iterIgnore:
+                    main.log.info("ONOS "+str(nodeNum) + " warming up - port down Link-to-graph: "+ str(ptDownLinkToGraph) + " ms")
+                else:
+                    portDownLinkToGraphNodeIter[node][i] = ptDownLinkToGraph
+                    main.log.info("ONOS "+str(nodeNum) + " port down Link-to-graph: "+ str(ptDownLinkToGraph) + " ms")
+
+            time.sleep(3)
+
+            main.step('Starting wireshark capture for port status up')
+            main.ONOSbench.tsharkGrep(tsharkPortStatus, tsharkPortUp)
+
+            time.sleep(5)
+            main.step('Enable port and obtain timestamp')
+            main.Mininet1.handle.sendline('sh ifconfig ' + interfaceConfig + ' up')
+            main.Mininet1.handle.expect('mininet>')
+
+            jsonStrPtUp = []
+            for node in range (0, clusterCount):
+                metricsPortUp = CLIs[node].topologyEventsMetrics()
+                jsonStrPtUp.append(metricsPortUp)
+
+            time.sleep(5)
+            main.ONOSbench.tsharkStop()
+
+            time.sleep(3)
+
+            fPortUp = open(tsharkPortUp, 'r')
+            fLine = fPortUp.readline()
+            objUp = fLine.split(' ')
+            if len(fLine) > 0:
+                timestampBeginPtUp = int(float(objUp[1]) * 1000)
+                if timestampBeginPtUp < 1400000000000:
+                    timestampBeginPtUp = int(float(objUp[2]) * 1000)
+            else:
+                main.log.info('Tshark output file returned unexpected' + ' results.')
+                timestampBeginPtUp = 0
+            fPortUp.close()
+
+            for node in range(0, clusterCount):
+                nodeNum = node+1
+                metricsUp = CLIs[node].topologyEventsMetrics
+                jsonStrUp = metricsUp()
+                jsonObj = json.loads(jsonStrPtUp[node])
+
+                if jsonObj:
+                    graphTimestamp = jsonObj[graphTimestampKey]['value']
+                    deviceTimestamp = jsonObj[deviceTimestampKey]['value']
+                    linkTimestamp = jsonObj[linkTimestampKey]['value']
+                else:
+                    main.log.error( "Unexpected json object" )
+                    graphTimestamp = 0
+                    deviceTimestamp = 0
+                    linkTimestamp = 0
+
+
+                main.log.info('ptDownTimestamp: ' + str(timestampBeginPtDown))
+                main.log.info("graphTimestamp: " + str(graphTimestamp))
+                main.log.info("deviceTimestamp: " + str(deviceTimestamp))
+                main.log.info("linkTimestamp: " + str(linkTimestamp))
+
+                ptUpEndToEnd = int(graphTimestamp) - int(timestampBeginPtUp)
+                ptUpOfpToDevice = float(deviceTimestamp) - float(timestampBeginPtUp)
+                ptUpDeviceToLink = float(linkTimestamp) - float(deviceTimestamp)
+                ptUpLinkToGraph = float(graphTimestamp) - float(linkTimestamp)
+
+                if ptUpEndToEnd < upThresholdMin or ptUpEndToEnd >= upThresholdMax:
+                    main.log.info("ONOS " +str(nodeNum) + " surpassed threshold - port up End-to-end: "+ str(ptUpEndToEnd) + " ms")
+                elif i < iterIgnore:
+                    main.log.info("ONOS "+str(nodeNum) + " warming up - port up End-to-end: "+ str(ptUpEndToEnd) + " ms")
+                else:
+                    portUpEndToEndNodeIter[node][i] = ptUpEndToEnd
+                    main.log.info("ONOS "+str(nodeNum) + " port up End-to-end: "+ str(ptUpEndToEnd) + " ms")
+
+                if ptUpOfpToDevice < upThresholdMin or ptUpOfpToDevice >= upThresholdMax:
+                    main.log.info("ONOS " + str(nodeNum) + " surpassed threshold - port up Ofp-to-device: "+ str(ptUpOfpToDevice) + " ms")
+                elif i < iterIgnore:
+                    main.log.info("ONOS "+ str(nodeNum) + " warming up - port up Ofp-to-device: "+ str(ptUpOfpToDevice) + " ms")
+                else:
+                    portUpOfpToDevNodeIter[node][i] = ptUpOfpToDevice
+                    main.log.info("ONOS "+ str(nodeNum) + " port up Ofp-to-device: "+ str(ptUpOfpToDevice) + " ms")
+
+                if ptUpDeviceToLink < upThresholdMin or ptUpDeviceToLink >= upThresholdMax:
+                    main.log.info("ONOS " +str(nodeNum) + " surpassed threshold - port up Device-to-link: "+ str(ptUpDeviceToLink) + " ms")
+                elif i < iterIgnore:
+                    main.log.info("ONOS "+str(nodeNum) + " warming up - port up Device-to-link: "+ str(ptUpDeviceToLink) + " ms")
+                else:
+                    portUpDevToLinkNodeIter[node][i] = ptUpDeviceToLink
+                    main.log.info("ONOS "+str(nodeNum) + " port up Device-to-link: "+ str(ptUpDeviceToLink) + " ms")
+
+                if ptUpLinkToGraph < upThresholdMin or ptUpLinkToGraph >= upThresholdMax:
+                    main.log.info("ONOS " + str(nodeNum) + " surpassed threshold - port up Link-to-graph: " + str(ptUpLinkToGraph) + " ms")
+                elif i < iterIgnore:
+                    main.log.info("ONOS " + str(nodeNum) + " warming up - port up Link-to-graph: " + str(ptUpLinkToGraph) + " ms")
+                else:
+                    portUpLinkToGraphNodeIter[node][i] = ptUpLinkToGraph
+                    main.log.info("ONOS " + str(nodeNum) + " port up Link-to-graph: " + str(ptUpLinkToGraph) + " ms")
+
+        dbCmdList = []
+        for node in range(0, clusterCount):
+            portUpEndToEndList = []
+            portUpOfpToDevList = []
+            portUpDevToLinkList = []
+            portUpLinkToGraphList = []
+
+            portDownEndToEndList = []
+            portDownOfpToDevList = []
+            portDownDevToLinkList = []
+            portDownLinkToGraphList = []
+
+            portUpEndToEndAvg = 0
+            portUpOfpToDevAvg = 0
+            portUpDevToLinkAvg = 0
+            portUpLinkToGraphAvg = 0
+
+            portDownEndToEndAvg = 0
+            portDownOfpToDevAvg = 0
+            portDownDevToLinkAvg = 0
+            portDownLinkToGraphAvg = 0
+
+            # TODO: Update for more pythonic way to get list
+            # portUpDevList = [item for item in portUpDevNodeIter[node]
+            #        if item > 0.0]
+            for item in portUpEndToEndNodeIter[node]:
+                if item > 0.0:
+                    portUpEndToEndList.append(item)
+
+            for item in portUpOfpToDevNodeIter[node]:
+                if item > 0.0:
+                    portUpOfpToDevList.append(item)
+
+            for item in portUpDevToLinkNodeIter[node]:
+                if item > 0.0:
+                    portUpDevToLinkList.append(item)
+
+            for item in portUpLinkToGraphNodeIter[node]:
+                if item >= 0.0:
+                    portUpLinkToGraphList.append(item)
+
+            for item in portDownEndToEndNodeIter[node]:
+                if item > 0.0:
+                    portDownEndToEndList.append(item)
+
+            for item in portDownOfpToDevNodeIter[node]:
+                if item > 0.0:
+                    portDownOfpToDevList.append(item)
+
+            for item in portDownDevToLinkNodeIter[node]:
+                if item >= 0.0:
+                    portDownDevToLinkList.append(item)
+
+            for item in portDownLinkToGraphNodeIter[node]:
+                if item >= 0.0:
+                    portDownLinkToGraphList.append(item)
+
+            portUpEndToEndAvg = round(numpy.mean(portUpEndToEndList), 2)
+            portUpOfpToDevAvg = round(numpy.mean(portUpOfpToDevList), 2)
+            portUpDevToLinkAvg = round(numpy.mean(portUpDevToLinkList), 2)
+            portUpLinkToGraphAvg = round(numpy.mean(portUpLinkToGraphList), 2)
+
+            portDownEndToEndAvg = round(numpy.mean(portDownEndToEndList), 2)
+            portDownOfpToDevAvg = round(numpy.mean(portDownOfpToDevList), 2)
+            portDownDevToLinkAvg = round(numpy.mean(portDownDevToLinkList), 2)
+            portDownLinkToGraphAvg = round(numpy.mean(portDownLinkToGraphList), 2)
+
+            portUpStdDev = round(numpy.std(portUpEndToEndList), 2)
+            portDownStdDev = round(numpy.std(portDownEndToEndList), 2)
+
+            main.log.report(' - Node ' + str(node + 1) + ' Summary ---------------- ')
+            main.log.report(' Port up End-to-end ' +
+                    str(portUpEndToEndAvg) + ' ms')
+            main.log.report(' Port up Ofp-to-device ' +
+                    str(portUpOfpToDevAvg) + ' ms')
+            main.log.report(' Port up Device-to-link ' +
+                    str(portUpDevToLinkAvg) + ' ms')
+            main.log.report(' Port up Link-to-graph ' +
+                    str(portUpLinkToGraphAvg) + ' ms')
+
+            main.log.report(' Port down End-to-end ' +
+                    str(round(portDownEndToEndAvg, 2)) + ' ms')
+            main.log.report(' Port down Ofp-to-device ' +
+                    str(portDownOfpToDevAvg) + ' ms')
+            main.log.report(' Port down Device-to-link ' +
+                    str(portDownDevToLinkAvg) + ' ms')
+            main.log.report(' Port down Link-to-graph' +
+                    str(portDownLinkToGraphAvg) + ' ms')
+
+            dbCmdList.append("INSERT INTO port_latency_details VALUES('" +
+                    timeToPost + "','port_latency_results'," + jenkinsBuildNumber +
+                    ',' + str(clusterCount) + ",'baremetal" + str(node + 1) +
+                    "'," +
+                    str(portUpEndToEndAvg) +',' +
+                    str(portUpOfpToDevAvg) + ',' +
+                    str(portUpDevToLinkAvg) + ',' +
+                    str(portUpLinkToGraphAvg) + ',' +
+                    str(portDownEndToEndAvg) + ',' +
+                    str(portDownOfpToDevAvg) + ',' +
+                    str(portDownDevToLinkAvg) + ',' +
+                    str(portDownLinkToGraphAvg) +
+                    ');')
+
+        fResult = open(resultPath, 'a')
+        for line in dbCmdList:
+            if line:
+                fResult.write(line + '\n')
+
+        fResult.close()
+
+        # Delete switches from controller to prepare for next
+        # set of tests
+        main.Mininet1.deleteSwController('s1')
+        main.Mininet1.deleteSwController('s2')
+
+        main.log.info("Stopping mininet")
+        main.Mininet1.stopNet()
diff --git a/TestON/tests/SCPFportLat/SCPFportLat.topo b/TestON/tests/SCPFportLat/SCPFportLat.topo
new file mode 100644
index 0000000..aa43281
--- /dev/null
+++ b/TestON/tests/SCPFportLat/SCPFportLat.topo
@@ -0,0 +1,97 @@
+<TOPOLOGY>
+
+    <COMPONENT>
+
+        <ONOSbench>
+            <host>localhost</host>
+            <user>admin</user>
+            <password>onos_test</password>
+            <type>OnosDriver</type>
+            <connect_order>1</connect_order>
+            <COMPONENTS>
+                <home>~/onos</home>
+                <nodes>7</nodes> 
+            </COMPONENTS>
+        </ONOSbench>
+
+        <ONOS1cli>
+            <host>localhost</host>
+            <user>admin</user>
+            <password>onos_test</password>
+            <type>OnosCliDriver</type>
+            <connect_order>2</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS1cli>
+
+        <ONOS2cli>
+            <host>localhost</host>
+            <user>admin</user>
+            <password>onos_test</password>
+            <type>OnosCliDriver</type>
+            <connect_order>3</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS2cli>
+
+        <ONOS3cli>
+            <host>localhost</host>
+            <user>admin</user>
+            <password>onos_test</password>
+            <type>OnosCliDriver</type>
+            <connect_order>4</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS3cli>
+
+        <ONOS4cli>
+            <host>localhost</host>
+            <user>admin</user>
+            <password>onos_test</password>
+            <type>OnosCliDriver</type>
+            <connect_order>5</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS4cli>
+
+        <ONOS5cli>
+            <host>localhost</host>
+            <user>admin</user>
+            <password>onos_test</password>
+            <type>OnosCliDriver</type>
+            <connect_order>6</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS5cli>
+
+        <ONOS6cli>
+            <host>localhost</host>
+            <user>admin</user>
+            <password>onos_test</password>
+            <type>OnosCliDriver</type>
+            <connect_order>7</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS6cli>
+
+        <ONOS7cli>
+            <host>localhost</host>
+            <user>admin</user>
+            <password>onos_test</password>
+            <type>OnosCliDriver</type>
+            <connect_order>8</connect_order>
+            <COMPONENTS> </COMPONENTS>
+        </ONOS7cli>
+
+        <Mininet1>
+            <host>localhost</host>
+            <user>admin</user>
+            <password>onos_test</password>
+            <type>MininetCliDriver</type>
+            <connect_order>9</connect_order>
+            <COMPONENTS>
+                <arg1> --custom ~/mininet/custom/topo-perf-2sw.py </arg1>
+                <arg2> --topo mytopo</arg2>
+                <arg3> --switch ovsk,protocols=OpenFlow13</arg3>
+                <controller> remote </controller>
+            </COMPONENTS>
+        </Mininet1>
+
+    </COMPONENT>
+
+</TOPOLOGY>
+ 
diff --git a/TestON/tests/SCPFportLat/__init__.py b/TestON/tests/SCPFportLat/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/TestON/tests/SCPFportLat/__init__.py